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