Can someone explain to me why this is the accepted standard for adding to an object to the state in react as I can’t seem to find the answer I’m looking for.
const [artists, setArtists] = useState([]);
setArtists([
...artists,
{ id: nextId++, name: name }
]);
I feel like there should be a much easier way to do this other than artists.push()
which the react docs say is bad as you should treat the state as immutable instead of having to combine the old state with the new.
Similar to how zustand
state management works in the docs where it shows:
"The set function is to update state in the store. Because the state is immutable, it should have been like this:"
set((state) => ({ ...state, count: state.count + 1 }))
"However, as this is a common pattern, set actually merges state, and we can skip the …state part:"
set((state) => ({ count: state.count + 1 }))
Maybe I’m missing something, but could you explain a better/easier way for doing this or why this is the accepted standard and nothing easier can possibly exist?
2
Answers
"Easier" is subjective, so its difficult to answer without also being subjective.
Immer (https://immerjs.github.io/immer/update-patterns/) allows writing updates that feel more "natural" to a lot of developers. Immer is also used under the hood for Redux Toolkit.
Taking your example:
In Immer, it would look like:
React needs to check when a component needs to be updated, i.e. rendered.
To do this, react compares the props and the state before and after the state update. Comparing whole objects is much more complex than simply assuming that objects don’t change. So if you want to change the state of an object and pass in a new object instead of the manipulated old object, react knows immediately that something needs to be re-rendered.
In React, the state update function (e.g., setArtists in your example) replaces the current state with whatever you pass to it. It doesn’t merge the new state with the old state.
This explicitness in state management gives you more control but also means you have to manually merge the previous state with the new state, which can be a bit verbose, especially when adding to an array or updating an object property.
Other state management libraries provide a more straightforward merging mechanism because they are designed to handle complex state management and try to simplify these kinds of operations. But they add a layer of abstraction which might not always be necessary, especially for simpler apps. React prefers to give you the primitives and let you build your abstractions as you need them.
Of course, you could also write your own hook that implements the operations appropriately, if necessary, to avoid the overhead in the individual components. For arrays it could look like this
to use it
Maybe this workaround is more familiar to you, because it feels similar to the state handling and in my opinion it is more readable.