Reputation: 45
I stumbled upon a strange behaviour due to my lack of knowledge and experience. I have a Component called inside another, looking like this:
<CustomSelectionButtons
values={roleTypes}
label="Roles:"
onSelect={setUserRoles}
selected={roles}
/>
roles is a state variable, which is set with the onSelect method (setUserRoles). Here it is:
const setUserRoles = (button: AccountsAndRolesUnion) => {
const updatedRoles: AccountsAndRolesUnion[] = roles; //this is the state variable.
/* doing something with the local **updatedRoles** array */
setRoles(updatedRoles);
};
If I use the code like this, the component does not rerender after setRoles hook call. However, If I assign the local variable with roles.slice(), everything works as expected, the component is rerendered. So, this works:
const setUserRoles = (button: AccountsAndRolesUnion) => {
const updatedRoles: AccountsAndRolesUnion[] = roles.slice(); //slicing the state variable.
/* doing something with the local **updatedRoles** array */
setRoles(updatedRoles);
};
Can someone explain why this happens?
Upvotes: 1
Views: 2570
Reputation: 36
Slice creates a shallow copy of the original array, meaning that it returns a different object than roles
itself, even though its content may be the same.
When you do setRoles(roles)
, you are actually sending the exact same object in memory, and when React compares the props before deciding whether to re-render, it doesn't detect any difference. However, when you do setRoles(roles.slice())
, you pass a different variable in memory, so React figures there is a change in props and re-renders.
Upvotes: 1
Reputation: 6146
In your first example you use the same array instance to update the state.
React checks only shallow equality. It's not iterating over the items.
.slice()
creates a new array.
Always create a new array if your state is an array.
Upvotes: 1
Reputation: 5036
This happens because const updatedRoles=roles
creates another name for the exact same object in memory, so everything you do with updatedRoles
is reflected in roles
, in other words you mutate the state. roles.slice()
on the other hand creates a new independent object. When you pass this new object to setState
React can see the difference and trigger re-render, but when you just pass it roles
by another name, React doesn't see any need to do anything because updatedRoles
and roles
are the same thing.
Upvotes: 1