Nested state in React can prove a real pain to work with, especially when dealing with developers who are used to thinking in terms of mutability rather than immutability. All too often you will be working with deeply nested objects and attempting to update specific values while leaving others unchanged in the remaining parts of the state. Quite tricky to do.
This post is walking you through how to update nested state properties properly in React and touches on common pitfalls along the way as well as sharing some tips that can make state management easier.
In React, you should not modify your states directly. This implies that you are not allowed to change properties of an object or elements of an array directly. You should always create a new copy of objects or arrays whenever they need to be changed.
This is significant because React relies on state immutability to determine changes and consequently, it triggers rerenders. Direct changes in the state could sometimes result in behavior that nobody expects.
Consider this example:
const [user, setUser] = useState({
name: "John",
address: {
city: "New York",
zip: "10001",
},
});
Suppose you want to change the city property of user.address.This below is not a good practice & it’s incorrect
user.address.city = "Los Angeles"; // ❌ Replace directly in place
setUser(user);
This code directly changes your user object and might not force a rerender for your state to be consistent with your ReactJS application.
To perform the correct updates on the nested state, you need to make a new copy of each level of the object hierarchy you want to update. For our example, we will make a copy of the user object and its nested address object and then update the city property.
Here's how you do that with Functional Updates:
A safe way to update state based on the previous state is by using a function update. This guarantees that React will always apply the latest value of state when it runs an update.
setUser((prevUser) => ({
...prevUser,
address: {
...prevUser.address,
city: "Los Angeles",
},
}));
Here is What's happening in this code, step by step:
Here’s a breakdown:
We create a new reference for user and for user.address by copying each level so ReactJS will be aware that its state has been altered. That is an important part of how React can recognize that the change has indeed occurred, thus redraw the component.
Updating Deeply Nested State Using Utility Libraries: Such nesting gets verbose and complex if the levels are multiplied multiple fold. Luckily, some utility libraries simplify this entire process:
1. Lodash's _.set:
Lodash has a lot of useful functions for handling objects, including the set function to change deeply nested properties.
import _ from "lodash";
setUser((prevUser) => _.set({ ...prevUser }, "address.city", "Los Angeles"));
2. Immer:
This is a thirdparty library that allows you to change nested state in a much more declarative way because it handles immutability under the hood.
import produce from "immer";
setUser((prevUser) =>
produce(prevUser, (draft) => {
draft.address.city = "Los Angeles";
})
);
You will write code as if you were directly changing the object, but immer will take care of creating the right copies and ensuring the right immutability for such nested structures.
Apparently, updating a nested property of state in React is not such a 'piece of cake', and yet once you get what immutability is about and how functional update works, everything will be alright. For now, here's a brief recap:
Never mutate state directly, especially when dealing with nested structures.
To retrieve the latest state in nested properties, simply use a functional update.
For more complex scenarios with updates of the state, try to use utility libraries, such as lodash or immer.
Flat state as much as possible. If the app state becomes too complex, use a state management library.
Try these things out to see how they go in your own ReactJS projects. Remember: a bit of extra care with immutability goes a long way toward building predictable and maintainable React applications. Happy coding!
Ready to transform your business with our technology solutions? Contact Us today to Leverage Our ReactJS Expertise.
0