The web content explains how to properly update an array in React state using hooks, emphasizing the use of .concat() and the spread operator ... over .push() to maintain immutability.
Abstract
The article discusses the common React practice of updating state arrays using hooks, particularly useState. It clarifies why the .push() method is ineffective for state updates, as it mutates the array directly and doesn't trigger a re-render. Instead, the author recommends using .concat() or the spread operator to create a new array, ensuring immutability and proper state management. The article also covers the importance of using a "wrapper function" when updating state to avoid potential bugs due to stale state references. The concepts of immutability and the correct usage of React Hooks are reinforced through examples and a live demo.
Opinions
Directly mutating state with .push() is discouraged in React as it violates the principle of immutability.
The use of .concat() or the spread operator ... is preferred for updating arrays in state because they return a new array without altering the original.
Immutability in React helps with performance and prevents unwanted side effects.
A "wrapper function" is recommended when updating state to ensure the update is based on the most current state value.
The author emphasizes the importance of understanding how React Hooks work to effectively manage state.
The article suggests that adhering to best practices for state management, such as immutability, can lead to more robust and bug-free applications.
How to Add to an Array in React State using Hooks
The .push() function will not work, but the .concat() function can update state in React when state is a JavaScript array — as can …, the spread operator.
“One of the most common questions is how to add an item to an array in React state. Since you are not allowed to mutate the state directly, you cannot simply push an item to an array.” — Robin Wieruch in a 2018 blog post
When React state is an array, it is not obvious how to add items to the array, such as when trying to update state using React Hooks.
This question might come up when state is a list of items. In this article, I explore an example of the recent search queries entered by the user.
A more complex example would be storing a list of JavaScript objects as an array in the React state, such as student records.
The question is how do I add an item to the array in React State?
A typical code example using React Hooks to keep a list of items would initialize React state using a call like useState([]).
“useState is a Hook […] We call it inside a function component to add some local state to it. React will preserve this state between re-renders.” — React Docs
That is called using the useState Hook. In this example, the command useState([]) initializes the state to contain the empty array, [].
In the next section I try various methods to add to the array that is currently in the React state — accessible via searches and mutable via setSearches.
The reason it does not work is that I tried to modify the React state directly — while .push() does modify the array found in the variable searches, that modification never “hooks” over to React to update on the re-render.
React Hooks require a specific setter function, which is the second part returned from the useState call originally, what I called setSearches.
“useState returns a pair: the current state value and a function that lets you update it.” — React Docs
The JavaScript spread operator (…) helps when copying and combining arrays, so it can also be used to add an item to an array in React state.
[...searches, query] to append an item to the end of the array
[query, ...searches] to prepend an item to the front of the array
Note in the code example I also used a wrapper function — instead of passing the state, I passed a call-back function taking in the current state.
setSearches(searches => searches.concat(query)) with .concat()
setSearches(searches => [...searches, query]) with … spread
In the wrapper function passed to the setSearches Hook, I update the state searches by returning the updated state: searches.concat(query) using .concat() or [...searches, query] with the spread operator e>….
Using a wrapper function inside the React Hooks setter function is a better solution than the implementation just using .concat()or ….
Jasper Dunn wrote in response to this article about the advantages of always using a wrapper function when working with React state:
“The value of `searches` accessed from the initial hook may be different than is expected, and this could [cause] unwanted side effects.” — Jasper Dunn
Use of the wrapper function is highly encouraged so that the current state is accessed when the re-render actually occurs, not at some other time.
The wrapper function is also called a callback function, since it refers to passing a function to another function.
Obviously, at some point in the app, we are updating the state — it just happens by providing React a new state, overwriting the old state.
That means we cannot mutate the state in-place, we update it by replacing it with a new state — for an array, using .concat() or spread ….
Using an immutable state helps bug-proof our code, in addition to signaling the Virtual DOM to update when the reference changes.
Immutability ensures we only ever mutate the state when we actually want to — not accidentally, where we might lose user data or have other terrible things happen in production. 😱