If this helped simplify a tricky concept, feel free to like or share—it might help someone else too!
I’m organizing a closet for a family of nesting dolls (what is my life lol). Each doll represents a part of my application’s state: the biggest doll is the overall app, and inside her are smaller dolls that represent features or components. Inside those smaller dolls, there are even tinier ones representing specific pieces of data.
Now, every time one of these little dolls wants to change, I can’t just swap her out and shove a new doll in the middle of the stack. That would cause chaos—heads might roll! Instead, I carefully open the stack from the outside, replace the specific doll, and then put everything back together exactly as it was, just with the updated doll inside.
In Angular, I do this using immutability. If I have a nested state object, I don’t modify the existing one directly because that could confuse Angular about what changed. Instead, I make a copy of the larger doll (state), then update the specific doll (nested property), and finally reassemble the whole stack.
To make it efficient, I use helper tools like the spread operator ({ ...state }
) or libraries like Immer.js, which help me handle these nested updates without creating a mess of code. This way, Angular knows exactly which doll changed and can efficiently update only the parts of the UI that need refreshing.
Continuing with our nesting doll closet, here’s how I handle those updates with code. Let’s say my application state looks like this:
const appState = {
user: {
name: "Anna",
preferences: {
theme: "dark",
language: "English"
}
},
products: []
};
If I want to update the theme
without mutating the original appState
, I carefully “open the dolls” and “replace the right one” like this:
Example 1: Using the Spread Operator
const updatedState = {
...appState, // Copy the outer state
user: {
...appState.user, // Copy the user object
preferences: {
...appState.user.preferences, // Copy the preferences object
theme: "light" // Update the theme
}
}
};
Here, I start from the top and copy each level, updating only the part that needs to change. This keeps the original appState
intact, ensuring immutability.
Example 2: Using a Utility Function
To make this more reusable, I might use a helper function:
function updateNestedState(state, path, value) {
if (path.length === 1) {
return { ...state, [path[0]]: value };
}
const [key, ...rest] = path;
return {
...state,
[key]: updateNestedState(state[key], rest, value)
};
}
const updatedState = updateNestedState(appState, ["user", "preferences", "theme"], "light");
This utility lets me specify a “path” to the doll I want to update, making it flexible for various nested structures.
Example 3: Using Immer.js
For complex state updates, libraries like Immer.js
make this process even easier:
import produce from "immer";
const updatedState = produce(appState, draft => {
draft.user.preferences.theme = "light"; // Directly modify the draft
});
Immer simplifies the process by letting me write updates as if they’re mutable, while handling the immutability behind the scenes.
Key Takeaways
- Immutability is crucial: Avoid direct mutations so Angular (or any state-based framework) can efficiently detect changes.
- Spread operator is great for small updates, but it can get verbose with deeply nested objects.
- Utility functions or libraries like
Immer.js
simplify handling complex nested structures. - Always test state updates to ensure the original object remains untouched.
By treating state updates like carefully managing those nesting dolls, I can keep my code clean, efficient, and easy to maintain.
Leave a Reply