30/04/2025
If you’ve ever worked on a large React app, you’ve probably run into this beast: deeply nested state. Managing and updating such state structures without blowing up performance or making your code unreadable can be painful.
Let’s learn more about the problem and fixes for it.
The Problem: Imagine you have this state:
const [profile, setProfile] = useState({
user: {
name: 'Texionm',
settings: {
notifications: {
email: true,
sms: false
}
}
}
});
Now you want to update sms to true. Doing this manually:
setProfile(prev => ({
...prev,
user: {
...prev.user,
settings: {
...prev.user.settings,
notifications: {
...prev.user.settings.notifications,
sms: true
}
}
}
}));
That’s a lot of spreading just to change one value.
The Fixes:
1. Use Immer.js This tiny library lets you write “mutating” code while keeping immutability under the hood:
import produce from 'immer';
setProfile(prev =>
produce(prev, draft => {
draft.user.settings.notifications.sms = true;
})
);
Cleaner. Safer. No nested spreads.
2. Split Your State If parts of your state are independent, split them up into separate useState or useReducer hooks. This reduces the need for deep updates.
3. Redux Toolkit For larger apps, consider using Redux Toolkit. They support immutable updates with less boilerplate and better performance through selector-based subscriptions.
Bonus: Optimize Rendering
Updating a deep state can cause unnecessary re-renders. Use:
React.memo to prevent unnecessary child re-renders.
useCallback and useMemo to avoid recreating props.
useContextSelector (via use-context-selector) to make your context smarter.
Conclusion: Deeply nested states are common in real-world apps, but it doesn’t have to be a nightmare. With tools like Immer or Redux Toolkit, and smart optimization strategies, you can keep your React apps clean, fast, and maintainable.
Have you faced this in your projects? Let us know how you handled it!