如何在React中从一个数组中删除一个项目

172 阅读6分钟

在这个反应教程中,我们将回顾一些事情,然后看看如何从一个数组中删除一个项目。数组中的项目数量决定了有多少项目组件显示在页面上。我们希望能够在一个特定的项目上点击一个按钮,并让React自动从页面上删除它。但首先,让我们回顾一下React中的几个关键概念。


React中的道具与状态

在组件中,我们使用存储在props对象中的一个值来初始化状态。需要记住的一点是props和state的区别是什么。它们都持有数据,但有什么区别呢?props对象是用来给一个组件提供数据的。另一方面,状态可以被认为是私有数据。它是本地的,只能在那个特定的组件中访问。这意味着其他组件不能直接访问某个特定组件的状态。所以我们可以看到在中,我们在元素上设置了一些属性。
react set attribute prop

现在,在组件中,我们可以接受props对象所携带的数据,并使用它来初始化组件的状态!
react initialize state using props

所以我们可以看到,每个组件都有自己的状态,这些状态是本地的,并且是自己的隐私,但是我们也可以通过使用props来分享数据给另一个组件。事实上,有些组件甚至可能根本就没有任何状态,但仍然可以通过props接受数据。


Props是只读的

关于props的另一个关键点是,它是只读的。如果你试图打破这个规则,做这样的事情。

handleIncrement = e => {
  this.props.value = 1;
  this.setState({ count: this.state.count + 1 });
};

你会遇到一个错误。

TypeError:不能分配给对象'#'的只读属性'value'。

所以道具是组件的输入。你不能修改存储在props中的值,只能读取它们。这就是为什么你将看到的模式是从props中读取数据并将其分配到状态中。然后你可以根据需要修改存储在状态中的值。


提起和处理事件

现在我们将看到如何通过引发和处理一个事件来从页面上删除一个组件。首先,让我们在标记中添加那个按钮。


item.jsx

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>

            <button className="btn btn-lg btn-outline-danger ml-4">
              Delete
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

就这样,现在我们有了一个按钮,可以在每个项目的渲染上删除一个项目。
react delete button


React的关键概念

拥有状态的组件,应该是修改状态的那个。

删除按钮存在于组件上。然而,保存页面上的列表的状态是在组件中。因此,如果我们想修改这个状态(从数组中删除一个项目),应该在这个组件中完成。我们如何做到这一点呢?通过引发一个事件,然后处理该事件。让我们看一张图。
react raise event handle event

我们可以让Item组件引发一个叫做onDelete的事件,然后在Item组件中,我们可以用handleDelete()方法处理这个事件。


使用道具传递一个事件处理程序

这是一个非常酷的概念。我们将在中创建事件处理程序,然后通过props向传递一个对它的引用。首先,我们可以定义一个简单的 handleDelete() 函数。

items.jsx

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };

  handleDelete = () => {
    alert("Button Clicked!");
  };

  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item 
          key={item.id} 
          value={item.value} 
          onDelete={this.handleDelete} 
          />
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

现在,由于我们已经通过props传递了一个引用,我们可以像这样在中访问该函数的引用。
item.jsx

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>

            <button
              onClick={this.props.onDelete}
              className="btn btn-lg btn-outline-danger ml-4"
            >
              Delete
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

点击删除按钮显示,事件处理程序实际上是在工作。我们现在只输出一个警报,但一分钟后我们将使用过滤器函数从项目列表中删除一个项目。

所以,当你有这种情况时,是子程序 引发了事件 程序处理了该事件


用filter()删除一个项目

在子组件中,我们需要把我们想删除的项目的ID传递给父组件。否则,React将不知道要删除哪个项目。正如我们之前回顾的,这里是使用箭头函数传递参数的方法。

item.jsx

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>

            <button
              onClick={() => this.props.onDelete(this.props.id)}
              className="btn btn-lg btn-outline-danger ml-4"
            >
              Delete
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

现在在父组件中,我们需要更新handleDelete()函数以接受该id作为参数。此外,我们需要使用过滤器函数来创建一个新的项目数组,其中不包含被点击的项目。然后我们必须调用setState()函数来更新状态。

items.jsx

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };

  handleDelete = itemId => {
    const items = this.state.items.filter(item => item.id !== itemId);
    this.setState({ items: items });
  };

  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item
            key={item.id}
            value={item.value}
            onDelete={this.handleDelete}
            id={item.id}
          />
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

现在,如果我们点击某个项目的删除按钮,它就从页面上被删除了


一个更简洁的传递道具的方法

当传递一个以上的道具时,你可以通过使用一个对象作为道具来清理你的代码。

因此,不要像这段代码中那样为每个道具设置一个单独的属性。

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };

  handleDelete = itemId => {
    const items = this.state.items.filter(item => item.id !== itemId);
    this.setState({ items: items });
  };

  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item
            key={item.id}
            value={item.value}
            onDelete={this.handleDelete}
            id={item.id}
          />
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

我们可以像这样直接传递对象本身。

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };

  handleDelete = itemId => {
    const items = this.state.items.filter(item => item.id !== itemId);
    this.setState({ items: items });
  };

  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item 
          key={item.id} 
          onDelete={this.handleDelete} 
          item={item} 
          />
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

这意味着你还需要更新item.jsx,用this.props.item.value而不是this.props.value来引用道具*。*

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.item.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>

            <button
              onClick={() => this.props.onDelete(this.props.item.id)}
              className="btn btn-lg btn-outline-danger ml-4"
            >
              Delete
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

最后,让我们测试一下增量和删除按钮,以确保一切仍然工作得很好。

当使用React时,你不应该直接突变状态。如果一个对象(或数组,它也是一个对象)被改变,你应该创建一个新的副本。

其他人建议使用Array.prototype.splice(),但该方法会改变数组,所以最好不要在React中使用splice()。

最简单的是使用Array.prototype.filter()来创建一个新的数组。


如何在React中从数组中删除一个项目 摘要

在React中要从页面中删除一个项目,你需要采取几个步骤。在我们的例子中,我们首先要处理在子组件和父组件之间引发和处理事件。然后我们看到,在处理父组件中的事件时,我们使用过滤器函数来创建一个新的项目数组,其中不包括被点击的那个。最后,我们使用setState()函数来覆盖现有的状态,新的状态不再有被点击的项目。