3 An easy way to treat a state array as immutable these days is: let list = Array.from(this.state.list); list.push('woo'); this.setState({list}); Modify to your style preferences of course. – basicdays Mar 3, 2017 at 16:57 Is there really a case where it happens ? If we simply do this.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) ) – Albizia Jul 24, 2019 at 20:25 @Johncy If you would like to get [['test','test'],['new','new']] try: this.setState({ tableData: [...this.state.tableData, ['new', 'new']] – Ridd Sep 23, 2018 at 17:52
const initialState = [{
name: "foo",
counter: 0
},
{
name: "far",
counter: 0
},
{
name: "faz",
counter: 0
}
];
const [state, setState] = useState(initialState);
const clickButton = () => {
// 1. Make a shallow copy of the array
let temp_state = [...state];
// 2. Make a shallow copy of the element you want to mutate
let temp_element = {
...temp_state[0]
};
// 3. Update the property you're interested in
temp_element.counter = temp_element.counter + 1;
// 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
temp_state[0] = temp_element;
// 5. Set the state to our new copy
setState(temp_state);
}
Your push
will mutate the state directly and that could potentially lead to error prone code, even if you are "resetting" the state again afterwards. For example, it could lead to that some lifecycle methods like componentDidUpdate
won’t trigger.
The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:
this.setState(prevState => ({
arrayvar: [...prevState.arrayvar, newelement]
}))
Alternative syntax for earlier React versions
You can use concat
to get a clean syntax since it returns a new array:
this.setState({
arrayvar: this.state.arrayvar.concat([newelement])
})
Easiest, if you are using ES6
.
initialArray = [1, 2, 3];
newArray = [...initialArray, 4]; // --> [1,2,3,4]
New array will be [1,2,3,4]
to update your state in React
this.setState({
arrayvar: [...this.state.arrayvar, newelement]
});
The first question can be answered with React's component constructor by simply initializing the state as as empty array: Before we are going to manipulate a JavaScript array in React state, let's recap state in React shortly. State in React can be initialized in the constructor of a React component and afterward used by accessing it via the React component's class instance with the this object. The following tutorial shows you array manipulation in React with class components. If you are interested in seeing this with function components and React Hooks, check out the following guides:
import React, { Component } from 'react';
class App extends Component { constructor(props) { super(props);
this.state = { list: [1, 2, 3], }; }
render() { return ( <div> <ul> {this.state.list.map(item => ( <li key={item}>{item}</li> ))} </ul> </div> ); }}
export default App;
import React, { Component } from 'react';
class App extends Component { constructor(props) { super(props);
this.state = { list: [], }; }
render() { return ( <div> <ul> {this.state.list.map(item => ( <li key={item}>{item}</li> ))} </ul> </div> ); }}
export default App;
The map()
method in the render()
method, that is used to display the items of the array, does work, because it iterates over an empty array and thus returns no item for it. The iterator function in the map method isn't called. Let's say you would have a null state for your array instead, then you would have to have a fallback for your map method in your render method:
import React, { Component } from 'react';
class App extends Component { constructor(props) { super(props);
this.state = { list: null, }; }
render() { return ( <div> <ul> {(this.state.list || []).map(item => ( <li key={item}>{item}</li> ))} </ul> </div> ); }}
export default App;
You set an empty array as React state for the component by having a clear method for it. Building on top of this example, the third question, which asks how to create an initial array state, can be answered too. You have already seen how an initial array is set to state in the component's constructor. How would you reinitialize the initial state again, basically a reset of the state, after you have manipulated the state already? You can extract the initial state and then set it again whenever you want.
import React, { Component } from 'react';
const list = [1, 2, 3];
class App extends Component { constructor(props) { super(props);
this.state = { list, }; }
onClearArray = () => { this.setState({ list: [] }); };
onResetArray = () => { this.setState({ list }); };
render() { return ( <div> <ul> {this.state.list.map(item => ( <li key={item}>{item}</li> ))} </ul>
<button type="button" onClick={this.onClearArray}> Clear Array </button>
<button type="button" onClick={this.onResetArray}> Reset Array </button> </div> ); }}
export default App;
The state reset was only applied to the array. But you can apply it to your entire state too by extracting the whole initial state object.
import React, { Component } from 'react';
const INITIAL_STATE = { list: [1, 2, 3],};
class App extends Component { constructor(props) { super(props);
this.state = INITIAL_STATE; }
onClearArray = () => { this.setState({ list: [] }); };
onResetArray = () => { this.setState({ ...INITIAL_STATE }); };
render() { return ( <div> <ul> {this.state.list.map(item => ( <li key={item}>{item}</li> ))} </ul>
<button type="button" onClick={this.onClearArray}> Clear Array </button>
<button type="button" onClick={this.onResetArray}> Reset Array </button> </div> ); }}
export default App;
To update an object in a state array, call the map() method to iterate over the array and update the object that matches the condition. We can use the Array.filter method to remove an object from a state array in React. To update an array of objects state in React: On each iteration, we check if the id of the object is equal to 2, and if it is, we update its name and country properties.
Copied!import {useState} from 'react';
export default function App() {
const initialState = [
{id: 1, name: 'Alice', country: 'Austria'},
{id: 2, name: 'Bob', country: 'Belgium'},
];
const [employees, setEmployees] = useState(initialState);
// ✅ Add an object to a state array
const addObjectToArray = obj => {
setEmployees(current => [...current, obj]);
};
// ✅ Update one or more objects in a state array
const updateObjectInArray = () => {
setEmployees(current =>
current.map(obj => {
if (obj.id === 2) {
return {...obj, name: 'Sophia', country: 'Sweden'};
}
return obj;
}),
);
};
// ✅ Remove one or more objects from state array
const removeObjectFromArray = () => {
setEmployees(current =>
current.filter(obj => {
return obj.id !== 2;
}),
);
};
return (
<div>
<button
onClick={() =>
addObjectToArray({
id: Math.random(),
name: 'Carl',
country: 'Canada',
})
}
>
Add employee
</button>
<button onClick={updateObjectInArray}>Update object in array</button>
<button onClick={removeObjectFromArray}>Remove object from array</button>
{employees.map(employee => {
return (
<div key={employee.id}>
<h2>name: {employee.name}</h2>
<h2>country: {employee.country}</h2>
<hr />
</div>
);
})}
</div>
);
}
Copied!
const addObjectToArray = obj => {
setEmployees(current => [...current, obj]);
};
addObjectToArray({
id: 3,
name: 'Carl',
country: 'Canada',
})
Copied!
const updateObjectInArray = () => {
setEmployees(current =>
current.map(obj => {
if (obj.id === 2) {
return {
...obj,
name: 'Sophia',
country: 'Sweden'
};
}
return obj;
}),
);
};
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. 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. 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:
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checks: []
};
}
}
this.setState({
checks: ['A']
});
this.setState({
checks: ['A', 'B']
});
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>
);
}
}
constructor(props) {
super(props);
this.state = {
countries: ['China', 'Cambodia', 'Myanmar']
}
}
If we want to use arrays or objects in our React state, we have to create a copy of the value before modifying it. This is a cheat sheet on how to do add, remove, and update items in an array or object within the context of managing React state. Just wrote How to update an array of objects in React state Edit: Thanks for this cheatsheet, nice to have it open when fighting with array states. would also be curious about the answer - but i think your approach is not a bad approach - just different. Why the author didnt took this into account would be inetersting ...
const [todos, setTodos] = useState([]);
const handleAdd = (todo) => {
const newTodos = todos.slice();
newTodos.push(todo);
setTodos(newTodos);
}
const handleAdd = (todo) => {
const newTodos = [...todos];
newTodos.push(todo);
setTodos(newTodos);
}
const handleRemove = (todo) => {
const newTodos = todos.filter((t) => t !== todo);
setTodos(newTodos);
}
const handleUpdate = (index, todo) => {
const newTodos = [...todos];
newTodos[index] = todo;
setTodos(newTodos);
}
With method one we are using slice , slice(start, length) we grab all the elements till the given index. After that we append the new element, lastly using slice(index + 1) we are taking the remaining items from the original array and concentrate everything using ES6 array destructing. With method one we are simply using ES6 array destructing to shallow clone the array and then mutating the new array by deleting the element. With method 2 we are using slice to shallow clone the array. With method one we are simply using ES6 array destructing and appending an element to it. Where as method 2 goes old school using slice and push methods.
// Method 1 -> Use array destructure
const addUser = () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [...users, newUser];
setUsers(newUsers);
};
// Method 2 -> Use slice method with combination of push method.
const addUser = () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = users.slice();
newUsers.push(newUser);
setUsers(newUsers);
};
// Method 1 -> Use array destructure
const removeUser = (index) => () => {
const newUsers = [...users];
newUsers.splice(index, 1);
setUsers(newUsers);
};
// Method 2 -> Use slice method.
const removeUser = (index) => () => {
const newUsers = users.slice();
newUsers.splice(index, 1);
setUsers(newUsers);
};
// Method 1 -> Use array destrcture.
const addAfter = (index) => () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [
...users.slice(0, index + 1),
newUser,
...users.slice(index + 1)
];
setUsers(newUsers);
};
// Method 2 -> Using splice
const addAfter = (index) => () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [...users];
newUsers.splice(index + 1, 0, newUser)
setUsers(newUsers);
};
In order to update the array in the state object, a new array object must be supplied to the input key using the setState method: The setState method will replace the existing array with the updated array, though this can also be achieved with a shorter syntax using the spread (...) operator: Note: In the above example, the list is updated as soon as you start typing because onChange updates the state that re-renders the App component. The concat method creates and returns a new array by combining the cart array and the input value.
1import React, { Component } from "react";
2import "./style.css";
3
4export default class App extends Component {
5 state = {
6 cart: ["Corn", "Potato"],
7 };
8
9 saveInput = (e) => {
10 this.setState({ input: e.target.value });
11 };
12
13 addNewItem = () => {
14 let { cart, input } = this.state;
15 cart.push(input);
16 // this.state.cart.push(this.state.input); // same as above, though bad practice
17 };
18
19 render() {
20 return (
21 <div>
22 <input
23 type="text"
24 onChange={this.saveInput}
25 />
26 <button onClick={this.addNewItem}> Add Item </button>
27 <ol>
28 {this.state.cart.map((subItems, sIndex) => {
29 return <li key={sIndex}> {subItems}</li>
30 })}
31 </ol>
32 </div>
33 );
34 }
35}
1 addNewItem = () => {
2
let {
cart,
input
} = this.state;
3 cart.push(input);
4 this.setState({
cart: cart
});
5
};
1 addNewItem = () => {
2 this.setState({
cart: [...this.state.cart, this.state.input]
});
3
};
1 addNewItem = () => {
2 this.setState((prevState, props) => ({
3 cart: [...prevState.cart, prevState.input],
4
}));
5
};
1 addNewItem = () => {
2 this.setState(prevState => ({
3 cart: [...prevState.cart, prevState.input],
4
}));
5
};
Using hooks, you can easily apply that to your state array, as shown below. The values can now be used to render a list of paragraph tags using the map function. With the introduction of hooks in React 16.8, functional components can now also handle state in a simplified way. The snippet below is the class-based <MyComponent/> written as a functional component. The useState hook is a function that takes in a default value as a parameter (which can be empty) and returns an array containing the state and a function to update it. Array destructuring is used to extract the values from the result of the useState function call. If you're integrating your React app with an API, you probably would like to store values obtained from the API call in an array in state without losing previous values the array contained. The spread operator helps you do that easily. Take a close look at the snippet below, which implements the spread operator in finalArray by "spreading" the contents of oldArray.
1import React from 'react'
2
3class MyComponent extends React.Component {
4 constructor(props){
5 super(props);
6 this.state = { date: new Date(), name: 'Kofi'};
7 }
8
9 render(){
10 return(
11 <div>
12 <p> Hello {this.state.name} , it is {this.state.toLocaleTimeString()
13 <p>Date: {this.state.date.toLocaleDateString()}
14 </div>
15 )
16 }
17}
1import React, {useState} from 'react';
2
3function MyComponent(){
4
5 const [date, setDate] = useState(new Date())
6 const [name, setName] = useState("Kofi");
7
8 return(
9 <div>
10 <p> Hello {date.name} , it is {date.toLocaleTimeString()
11 <p>Date: {date.toLocaleDateString()}
12 <button onClick={setDate(new Date())}></button>
13 </div>
14 )
15}
1
const oldArray = ['peter piper', 'picked a pair']
2
3
const finalArray = [...oldArray, 'of pickle pepper']
4
5 console.log(finalArray)
6
7 // (3) ["peter piper", "picked a pair", "of pickle pepper"]
1import React, { useState, useEffect } from 'react';
2
3const ProductsPage = () => {
4 const [productsList, setProductsList] = useState([]);
5 const [isLoading, setisLoading] = useState(true);
6
7 useEffect(() => {
8 fetch('http://127.0.0.1:8000/api/v1/products/all')
9 .then((res) => res.json())
10 .then((data) => setProductsList({...data}))
11 .then(setisLoading(false));
12 }, []);
13
14 return (
15 <>
16 <Header />
17 {isLoading ? (
18 <div className='spinner-border text-primary' role='status'>
19 {' '}
20 <span className='sr-only'>Loading...</span>{' '}
21 </div>
22 ) : (
23 Object.keys(productList).map(product => {
24 <p key={productList[product].id}>{productList[product].name}</p>
25 })
26 )}
27 </>
28 );
29};
30
31}