Using Immutable to update complex objects
March 13, 2018
Imagine you have an object with structure:
token: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
state: PropTypes.shape({
valid: PropTypes.bool,
rights: PropTypes.arrayOf(PropTypes.shape({
right: PropTypes.string,
subscribe: PropTypes.bool,
publish: PropTypes.bool,
})),
}),
}),
As for a “real world” example, we can display the object like this.
token: {
id: 1234,
name: 'Nuclear missle',
state: {
valid: true,
rights: [
{
right: 'test',
subscribe: true,
publish: true,
},
{
right: 'launch',
subscribe: true,
publish: true,
},
]
}
}
Now imagine that we need to revoke someones access to launch a nuclear missile. The *publish* boolean has to be set to *false* on token.state.rights[1]. If we’re using React’s default *setState* function, that could look like:
updateRight = (index, key, target) => {
this.setState({
token: {
...this.state.token,
state: {
...this.state.token.state,
rights: this.state.token.state.rights.map((right, rightIndex) => (
index !== rightIndex ? right : {
...right, [key]: target.value,
}
)),
},
},
});
};
// Input element
onChange={e => updateRight(index, 'read', e.target)}
We want to update the right within the token.state object without changing any other data in that state, which is the reason for all the spread operators scattered throughout.
This is where Immutable Collections make an entrance. Initially I was unsure of the advantages of using immutable sans performance gains. Now that I’ve explored the Collection object I’ve realized it’s a game changer in terms of accessing and storing complex data.
We can re-write that entire function above into:
updateRight = (keyPath, target) => {
this.setState({ token: this.state.token.setIn(keyPath, target.value) });
};
// Input element
onChange={e => updateRight(['state', 'rights', index, 'read'], e.target)}
This will now update the state correctly negating the need for all of the spread operators used in the first example.