8

I have a todo list that holds a delete button in a grandchild, that when clicked fires an event in the parent - I am wanting this event to delete the array entry corresponding to the grandchild clicked.

Parent (contains the array and my attempt at the function)

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 });

}

Any help would be appreciated

2
  • I'd caution against basing your delete function on name only, unless you're explicitly preventing users from creating multiple tasks with the same name somewhere else in your code (which also may not be ideal, if someone needs to do the same task 3 times, they may want it 3 times). An index/ID based solution would be a safer system to use. deleteTask should just accept the index of the task you're attempting to remove. Commented Jan 22, 2018 at 15:52
  • @jmcgriz I will be negating duplicate tasks, yes. Commented Jan 22, 2018 at 15:55

6 Answers 6

14

Two issues there:

  1. You're seeming to try to direct modify this.state.tasks. It's important not to do that, never directly modify this.state or any object on it. See "Do Not Modify State Directly" in the React documentation for state.

  2. You're passing an object to setState that is derived from the current state. It's important never to do that, too. :-) Instead, pass setState a function and use the state object it passes you when calling that function. From "State Updates May Be Asynchronous" in the documentation:

    Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state... [Instead]...use a second form of setState() that accepts a function rather than an object.

    (my emphasis)

I figure your remove on an array was intended to be hypothetical, but for the avoidance of doubt, arrays don't have a remove method. In this case, the best thing to do, since we need a new array, is to use filter to remove all entries that shouldn't still be there.

So:

deleteTask(taskToDelete) {
    this.setState(prevState => {
        const tasks = prevState.tasks.filter(task => task.name !== taskToDelete);
        return { tasks };
    });
}
Sign up to request clarification or add additional context in comments.

1 Comment

nice job sir, did great publicity of prevState by comments :)
6

You could simply filter the array :

this.setState(prevState => ({
    tasks: prevState.tasks.filter(task => task.name !== 'taskToDelete')
}));

Also when updating based on this.state, its better to use the function form because setState is async.

Comments

4

You can use filter to remove one object from an array following the immutable pattern (filter will create a new array) :

deleteTask(taskToDelete) {
    const newTaskArray = this.state.tasks.filter(task => task.name !== taskToDelete);
    this.setState({ tasks: newTaskArray });

}

Edit : codepend of the solution : https://codepen.io/Dyo/pen/ZvPoYP

19 Comments

You've got a typo.
You cannot pass an object to setState if it's based on current state. You must use the function callback style instead. More: reactjs.org/docs/state-and-lifecycle.html#using-state-correctly
@ArupRakshit: The entire "State Updates May Be Asynchronous" section, yes. Primarily: "Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state."
@T.J.Crowder ok you got me, you're probably right, i think i never had to use this because the use case was pretty simple, but can be problematic on complex apps. Sorry for being insisting.
@Dyo: No worries at all, hope it was useful. Happy coding!!
|
4

You can implement deleteTask method as below:

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. Find the index of taskToDelete.

B. Then use splice method to delete the item from the collection

C. Then call setState to update the state with tasks.

9 Comments

I didn't. That is why I wrote const {tasks} = this.state; @T.J.Crowder .
Yes I am trying to understand what are you trying to say here. Not still making any sense. Commented in another thread for the same.
+1, glad it helped you, there is one issue with the updated answer, use this: const tasks = prevState.tasks.slice(0); array reference :)
@MayankShukla Fixed. Thanks :)
Great fix Arup!
|
2

You can use higher order function Array#filter to delete the task.

let updatedTasks  =     this.state.tasks.filter(task => task.name !== taskToDelete);

this.setState({ tasks: updatedTasks });

9 Comments

You cannot pass an object to setState if it's based on current state. You must use the function callback style instead. More: reactjs.org/docs/state-and-lifecycle.html#using-state-correctly
@T.J. Crowder - why? setState takes an object, he is creating a new one, filter creates a new array. Sure the callback version is better as you know the state is up to date, but the above is valid (and similar is used in the docs).
@T.J.Crowder I am not mutating the state directly.
@dashton: See the link for why. And no, you won't find an example of calling setState with an object whose data is a modified version of this.state in the documentation.
My issue is the use of cannot. You clearly can, but shouldn't if the updates are asynchronous
|
0

I have followed below steps to delete a particular selected Object from the state array:

Here I am using a list of checkBoxes, when I am selecting a checkBox it will add it in the state array and when it gets de-selected then it will get deleted from the array.

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);
                }
            }
        }

I got stuck for this question and I am sharing my solution. Hope it will help you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.