如何使用对象属性从数组中删除对象 - React

8
我有一个待办事项列表,其中包含一个删除按钮在孙子组件中,当点击该按钮时,会在父组件中触发一个事件 - 我希望这个事件可以删除与所点击的孙子组件对应的数组条目。 父组件(包含数组和我的函数尝试)
const tasks = [
  { name: 'task1', isComplete: false },
  { name: 'task2', isComplete: true },
  { name: 'task3', isComplete: false },
]

// taskToDelete is the name of the task - doesn't contain an object
deleteTask(taskToDelete) {
    this.state.tasks.remove(task => task.name === taskToDelete);
    this.setState({ tasks: this.state.tasks });

}

任何帮助都将不胜感激。

我要警告的是,除非你明确防止用户在代码的其他地方创建具有相同名称的多个任务(这也可能不是理想的选择,如果有人需要执行相同的任务3次,他们可能希望它出现3次),否则不要仅基于名称编写删除函数。一个基于索引/ID的解决方案将是更安全的系统来使用。 deleteTask 应该只接受您要删除的任务的索引。 - jmcgriz
@jmcgriz 我会否定重复的任务,是的。 - physicsboy
6个回答

14

有两个问题:

  1. 你似乎试图直接修改this.state.tasks。重要的是,不要这样做,永远不要直接修改this.state或任何对象。请参阅React文档中关于状态的"Do Not Modify State Directly"

  2. 你正在将一个对象传递给setState,该对象源自当前状态。同样重要的是永远不要这样做。 :-) 相反,传递一个函数给 setState 并在调用该函数时使用它传递给你的状态对象。来自文档中"State Updates May Be Asynchronous"的引用:

    因为this.propsthis.state可能会异步更新,所以您不应该依赖它们的值来计算下一个状态... [相反]...使用第二种形式的setState(),它接受一个函数而不是一个对象。

    (我加重了语气)

我认为你对数组上的remove是假设性的,但为了避免疑问,数组没有remove方法。在这种情况下,最好的做法,因为我们需要一个新的数组,是使用filter来删除所有不应该仍然存在的条目。

所以:

deleteTask(taskToDelete) {
    this.setState(prevState => {
        const tasks = prevState.tasks.filter(task => task.name !== taskToDelete);
        return { tasks };
    });
}

1
干得好,先生。通过注释很好地宣传了 prevState :) - Mayank Shukla

6
你可以简单地过滤数组:
this.setState(prevState => ({
    tasks: prevState.tasks.filter(task => task.name !== 'taskToDelete')
}));

同时,在基于 this.state 更新时,最好使用函数形式,因为 setState异步的


4
您可以使用filter来按照不可变模式从数组中删除一个对象(filter将创建一个新数组):
deleteTask(taskToDelete) {
    const newTaskArray = this.state.tasks.filter(task => task.name !== taskToDelete);
    this.setState({ tasks: newTaskArray });

}

编辑:解决方案的CodePen链接:https://codepen.io/Dyo/pen/ZvPoYP


1
如果基于当前状态,您不能将对象传递给 setState。您必须改用函数回调样式。更多信息请参见:https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly - T.J. Crowder
我不理解你的意思,你总是需要向 setState() 方法传递一个对象。 - Dyo
2
@ArupRakshit:整个“状态更新可能是异步的”部分,是的。 主要是:“因为此.props和此.state可能会异步更新,所以您不应依赖它们的值来计算下一个状态。” - T.J. Crowder
1
@T.J.Crowder 好的,你说对了,我想我从来没有使用过这个,因为用例非常简单,但在复杂的应用程序中可能会有问题。抱歉我一直坚持己见。 - Dyo
1
@Dyo:完全不用担心,希望这对你有所帮助。愉快编程! - T.J. Crowder
显示剩余14条评论

4
您可以按照以下方式实现deleteTask方法:
deleteTask(taskToDelete) {
  this.setState((prevState, props) => {
    const tasks = [...prevState.tasks];
    const indexOfTaskToDelete = tasks.findIndex(
      task => task.name === taskToDelete
    );
    tasks.splice(indexOfTaskToDelete, 1);
    return { tasks };
  });
}

A. 找到taskToDelete的索引。

B. 然后使用splice方法从集合中删除该项。

C. 然后调用setState函数,更新状态为tasks


我没有这样做。这就是为什么我写了 const {tasks} = this.state; @T.J.Crowder。 - Arup Rakshit
是的,我正在努力理解你在这里想要表达什么。目前还没有明白。在另一个线程中已经发表了评论。 - Arup Rakshit
1
+1,很高兴能帮到你,更新后的答案有一个问题,请使用:const tasks = prevState.tasks.slice(0); 数组引用 :) - Mayank Shukla
1
@MayankShukla 已修复。谢谢 :) - Arup Rakshit
1
很棒的修复,Arup! - T.J. Crowder
显示剩余4条评论

2
你可以使用高阶函数 Array#filter 来删除任务。
let updatedTasks  =     this.state.tasks.filter(task => task.name !== taskToDelete);

this.setState({ tasks: updatedTasks });

1
如果基于当前状态,您不能将对象传递给 setState。您必须改用函数回调样式。更多信息请参见:https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly - T.J. Crowder
1
@T.J. Crowder - 为什么?setState需要一个对象,他正在创建一个新的对象,filter创建一个新的数组。当然,回调函数版本更好,因为您知道状态是最新的,但上面的代码是有效的(文档中也使用类似的方式)。 - dashton
1
@dashton:请查看链接了解原因。不,你在文档中找不到使用修改后的this.state数据作为对象调用setState的示例。 - T.J. Crowder
1
我的问题是使用“不能(cannot)”这个词。显然你可以,但如果更新是异步的,就不应该使用它。 - dashton
@dashton:如果你想让你的应用程序正常可靠地工作,就不能这样做。 - T.J. Crowder
显示剩余4条评论

0
我已经按照以下步骤从状态数组中删除了特定选定的对象:
在这里,我使用复选框列表,当我选择一个复选框时,它将添加到状态数组中,当它被取消选择时,它将从数组中删除。
if (checked) {
            var tempObject = { checkboxValue: data, label: title }
            this.state.checkBoxState.push(resTemp);
        } else {
            var element = data; //data is coming from different method.
            for (let index = 0; index < this.state.checkBoxState.length; index++) {
                if (element === this.state.checkBoxState[index].checkboxValue) {
                    this.state.checkBoxState.splice(index, 1);
                }
            }
        }

我在这个问题上卡住了,现在分享我的解决方案。希望能对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接