I am trying to find the best way to remove an element from an array in the state of a component. Since I should not modify the this.state
variable directly, is there a better way (more concise) to remove an element from an array than what I have here?:
onRemovePerson: function(index) {
this.setState(prevState => { // pass callback in setState to avoid race condition
let newData = prevState.data.slice() //copy array from prevState
newData.splice(index, 1) // remove element
return {data: newData} // update state
})
},
Thank you.
updated
This has been updated to use the callback in setState. This should be done when referencing the current state while updating it.
To remove an element from an array of objects in React: Use the filter() method to iterate over the array. On each iteration check if a certain condition is met. The filter method returns an array containing only the elements that satisfy the condition.
The cleanest way to do this that I've seen is with filter
:
removeItem(index) {
this.setState({
data: this.state.data.filter((_, i) => i !== index)
});
}
You could use the update()
immutability helper from react-addons-update
, which effectively does the same thing under the hood, but what you're doing is fine.
this.setState(prevState => ({
data: update(prevState.data, {$splice: [[index, 1]]})
}))
I believe referencing this.state
inside of setState()
is discouraged (State Updates May Be Asynchronous).
The docs recommend using setState()
with a callback function so that prevState is passed in at runtime when the update occurs. So this is how it would look:
Using Array.prototype.filter without ES6
removeItem : function(index) {
this.setState(function(prevState){
return { data : prevState.data.filter(function(val, i) {
return i !== index;
})};
});
}
Using Array.prototype.filter with ES6 Arrow Functions
removeItem(index) {
this.setState((prevState) => ({
data: prevState.data.filter((_, i) => i !== index)
}));
}
Using immutability-helper
import update from 'immutability-helper'
...
removeItem(index) {
this.setState((prevState) => ({
data: update(prevState.data, {$splice: [[index, 1]]})
}))
}
Using Spread
function removeItem(index) {
this.setState((prevState) => ({
data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
}))
}
Note that in each instance, regardless of the technique used, this.setState()
is passed a callback, not an object reference to the old this.state
;
Here is a way to remove the element from the array in the state using ES6 spread syntax.
onRemovePerson: (index) => {
const data = this.state.data;
this.setState({
data: [...data.slice(0,index), ...data.slice(index+1)]
});
}
I want to chime in here even though this question has already been answered correctly by @pscl in case anyone else runs into the same issue I did. Out of the 4 methods give I chose to use the es6 syntax with arrow functions due to it's conciseness and lack of dependence on external libraries:
Using Array.prototype.filter with ES6 Arrow Functions
removeItem(index) {
this.setState((prevState) => ({
data: prevState.data.filter((_, i) => i != index)
}));
}
As you can see I made a slight modification to ignore the type of index (!==
to !=
) because in my case I was retrieving the index from a string field.
Another helpful point if you're seeing weird behavior when removing an element on the client side is to NEVER use the index of an array as the key for the element:
// bad
{content.map((content, index) =>
<p key={index}>{content.Content}</p>
)}
When React diffs with the virtual DOM on a change, it will look at the keys to determine what has changed. So if you're using indices and there is one less in the array, it will remove the last one. Instead, use the id's of the content as keys, like this.
// good
{content.map(content =>
<p key={content.id}>{content.Content}</p>
)}
The above is an excerpt from this answer from a related post.
Happy Coding Everyone!
As mentioned in a comment to ephrion's answer above, filter() can be slow, especially with large arrays, as it loops to look for an index that appears to have been determined already. This is a clean, but inefficient solution.
As an alternative one can simply 'slice' out the desired element and concatenate the fragments.
var dummyArray = [];
this.setState({data: dummyArray.concat(this.state.data.slice(0, index), this.state.data.slice(index))})
Hope this helps!
You can use this function, if you want to remove the element (without index)
removeItem(item) {
this.setState(prevState => {
data: prevState.data.filter(i => i !== item)
});
}
You could make the code more readable with a one line helper function:
const removeElement = (arr, i) => [...arr.slice(0, i), ...arr.slice(i+1)];
then use it like so:
this.setState(state => ({ places: removeElement(state.places, index) }));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With