Reputation: 28810
I thought that useState
needed to have a new value for it to accept updates:
I have created thiscodesandbox to illustrate what I mean.
I have a nested data structure like this
const state = {
name: "parent",
data: {
isExpanded: false,
children: [
{ name: "one", data: { isExpanded: false } },
{ name: "two", data: { isExpanded: false } },
{ name: "three", data: { isExpanded: false } }
]
}
};
I then use useState
to cause rerenders when the state changes:
function App() {
const [tree, setTree] = useState(state);
const toggleExpanded = node => {
node.data.isExpanded = !node.data.isExpanded;
setTree(tree);
};
return (
<div className="App">
<h1>IsExpanded</h1>
{tree.data.children.map(child => (
<button onClick={() => toggleExpanded(child)}>
{child.name} - {child.data.isExpanded ? "on" : "off"}
</button>
))}
</div>
);
}
I am using the same reference each time when calling setTree
and the updates are happening.
I thought it needed a new reference each time for this to happen?
I might be missing something but is update not happening with the same reference.
Upvotes: 0
Views: 3108
Reputation: 81026
This is expected behavior. The setter returned by useState
works the same in this regard as the non-hook setState
. Any execution of it will trigger a re-render and it doesn't matter whether it is the same object reference or not.
If you just did:
node.data.isExpanded = !node.data.isExpanded;
without calling setTree
, then no visual update would occur because a re-render would not be triggered.
It is often recommended to avoid doing state updates by changing the existing state object because it can lead to certain types of bugs (e.g. if you have any logic that compares previous state to the new state, they will appear to always be the same if you do changes by mutating the existing objects). And the possible, subtle issues this could lead to would likely expand with future React concurrent mode features, but React does not prevent you from doing it.
Upvotes: 1
Reputation: 222503
It's preferable to have immutable state because this approach is idiomatic to React. But this will work with mutating existing state, too. setTree(tree)
triggers an update, it doesn't matter if tree
is same or different.
It works the same way with setState(sameState)
in class components, which is similar to forceUpdate
, which is generally discouraged.
Upvotes: 0