React/ReactJS: Update An Array State
As in any web application, a React application too can have multiple checkboxes and/or selects.
And the need to store such related input/option values as a homogenous collection necessitates the use of array states. But modifying them using setState()
is not the same as updating normal state variables. In this tutorial, we will learn how to update/manage a state which is an array.
Let us first create an example class component. Inside its constructor, define a state variable called checks
which is initialized to an empty array.
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checks: []
};
}
}
Now assigning values to it the way we do to normal variables inside the setState()
method will work only when you assign an array as a whole. This is shown below, where we assign an array containing a single element 'A'
to the state variable checks
.
this.setState({
checks: ['A']
});
But what happens when you want to add another element after 'A'
? What if you want to add the item 'B'
to the array? You have to assign back the whole new array as follows.
this.setState({
checks: ['A','B']
});
Of course we can go about this way (assign the array state to some temporary variable, add the new element to it and then setState()
it again with the newly created array), but there is a better alternative. An array state can be updated element-wise, the way push()
method works in JavaScript, using the spread operator.
Adding/Appending Array Elements
It takes the following format:
this.setState({
checks: [ ...this.state.checks, 'some element'],
});
Below is a full working example to show how it works. The component <CheckingFruits/>
renders three input checkboxes with values Apple
, Mango
and Pear
. On check of each input, the respective values are stored as elements of this.state.fruit
array.
class CheckingFruits extends Component {
constructor(props) {
super(props);
this.state = {
fruits: []
}
this.selectFruit = this.selectFruit.bind(this);
}
selectFruit(e) {
if(e.target.checked) {
this.setState({
fruits: [ ...this.state.fruits, e.target.value],
}, () => {
console.log(this.state.fruits);
});
}
}
render() {
return (
<div>
<form>
<input type="checkbox" id="apple" name="fruit" value="Apple"
onClick={this.selectFruit}/>Apple <br/>
<input type="checkbox" id="mango" name="fruit" value="Mango"
onClick={this.selectFruit}/>Mango <br/>
<input type="checkbox" id="pear" name="fruit" value="Pear"
onClick={this.selectFruit}/>Pear <br/>
</form>
</div>
);
}
}
How do we Change/Update a Specific Array Element?
But what if we need to update a particular array element? What if we need to replace an array element at index i
with a different value?
Consider the below state variable countries
which is an array consisting of three elements: China
, Cambodia
and Myanmar
.
constructor(props) {
super(props);
this.state = {
countries: ['China', 'Cambodia', 'Myanmar']
}
}
Suppose you want to change the first element China
(programatically); you want to replace it with some other value, say, Brunei
. Proceeding with the method described in the preceding section, even though you delete the first element and then append the array with some new desired value, it will not be placed at the first index. We need a different approach for this.
One solution is to use the react-addons-update package. Install it.
npm i react-addons-update
Then import it into your component. Now replacing the first element (index 0
) of the state array variable countries
with some different value can be achieved this way:
import update from 'react-addons-update';
...
...
this.setState(update(this.state, {
countries: {
[0]: {
$set: 'Brunei'
}
}
}));
The element at index 0
is now updated with the new value Brunei
.
So, for changing some element at index i
, it will be
this.setState(update(this.state, {
countries: {
[i]: {
$set: 'Brunei'
}
}
}));
Removing Array Elements
Now we would want to remove any unchecked element (the <input/>
value) from the array state. We start by getting the index of the unchecked element first, and then make use of the filter()
method. The below code illustrates it all, check the else {}
block.
selectFruit(e) {
if(e.target.checked) {
this.setState({
fruits: [ ...this.state.fruits, e.target.value],
});
} else {
let remove = this.state.fruits.indexOf(e.target.value);
this.setState({
fruits: this.state.fruits.filter((_, i) => i !== remove)
},
() => {
console.log('fruits', this.state.fruits);
}
);
}
}
Adding Array of Objects
Now you may need to store the id
of the <input/>
element too along with its value
on every check. You can bundle them together as an object literal
{
id: 'apple',
value: 'Apple'
}
and add it to the array state, straight-forward.
this.setState({
fruits: [ ...this.state.fruits, {
'id': e.target.id,
'value': e.target.value
}],
});
Removing Array of Objects
For removing an item which is an object, we again need to find the index of the checked element in the array state. We make use of the map()
method to get the index as shown below; the code is inside the else {}
block:
let remove = this.state.fruits.map(function(item) { return item.value; })
.indexOf(e.target.value);
this.setState({
fruits: this.state.fruits.filter((_, i) => i !== remove)
});