Work with arrays in React states - Remove, add and update items
In this guide, we will work with objects and arrays in React states. We will show how we can add items, delete items and also how to update properties in an object in the state. We will also explain a little bit on how one might think the state would update by mutating the state directly, and why it does not.
Mutation
Maybe this is the reason why you are here reading this article? Or maybe not. Anyway, the reason your state might not be updating as you want it to, can perhaps be that you are mutating the state, and if so, React will not detect that there has been a new state change, since it is the same object that are basically being the "new" value.
Example, if we create a state that should hold some number in an array as below snippet
const [items, setItems] = useState<number[]>([]);
and we then want to update it, and we do as following:
const addItem = () => { items.push(1); setItems(items); };
Do you think the component will be re-rendered? Answer is no. Since we are accessing the items directly and pushing a new number into it, and then setting the state with the same reference, React will not see this as a new value.
How about this one then?
const addItem = () => { const newItems = items; newItems.push(23); setItems(newItems); };
Will this cause a re-render? Answer is still no. Our newItems will still point to the same reference as items, and hence, React will not see this as a new value.
How about object then? Consider below snippet
const [person, setPerson] = useState<{ name: string }>({ name: "" }); const addPerson = () => { const newPerson = person; newPerson.name = "John"; setPerson(newPerson); };
Will our component re-render now then since we are changing a property on the object and not adding stuff? Answer is no. In next sections, we will show how we should do instead.
Add an item
We will begin with showing how we can add a number to our array.
If we consider the above example with our array state:
const [items, setItems] = useState<number[]>([]);
we can then use for example spread operator to copy the array, and then modify the copy and set the state with the copy which will have another reference in the memory.
const addItem = () => { const newItems = [...items]; newItems.push(23); setItems(newItems); };
and now, the state will update correct.
And for our object, we do a similar operation with the rest operator for objects:
const [person, setPerson] = useState<{ name: string }>({ name: "" }); const addPerson = () => { const newPerson = { ...person }; newPerson.name = "John"; setPerson(newPerson); };
and the component will re-render just fine.
Delete an item
If we want to delete an item from our array. We can utilize the filter function for arrays, that will create a new array based on our predicate.
Example below will remove all values higher than 5
const [items, setItems] = useState<number[]>([3, 4, 5, 6, 7, 8, 9]); const removeHigherThanFive = () => { const smallerNumbers = items.filter((item) => item < 5); setItems(smallerNumbers); };
For objects, there are no real difference from previous example. If we want to delete the whole object from the state, we can simply set the state to null or undefined for example.
const [person, setPerson] = useState<{ name: string }>({ name: "" }); const removeObject = () => { setPerson(null); };
And if we want to delete a certain property in the object, we do as previous section by setting the property to null for example
const removeName = () => { const newPerson = { ...person }; newPerson.name = null; setPerson(newPerson); };
Change properties
If we want to change the properties in an array, it can be done with for example the map function in arrays.
Consider that we want to multiple all our items with two for example
const [items, setItems] = useState<number[]>([3, 4, 5, 6, 7, 8, 9]); const multiply = () => { const multipliedNumbers = items.map((item) => item * 2); setItems(multipliedNumbers); };
And exactly as with the filter function, it will create a new array with a new reference.
For our object, it will be exactly as for our first example as well.
const [person, setPerson] = useState<{ name: string }>({ name: "Peter" }); const updatePerson = () => { const newPerson = { ...person }; newPerson.name = "John Doe"; setPerson(newPerson); };
The key part is the { ...person } operation since it will create a new object reference that will make React react to the change.
Summary
In this article, we showed the cause of mutating objects and arrays directly when working with states in React. And also how to do it properly.
In my own experience, strange bugs has occurred due to that there has been mutation happening deep down in the code, and that can sometimes be very hard to find.
I hope you enjoyed this guide, and thanks for reading!