I am using zustand for some state management. I notice with zustand, that if you update a state variable, to a clone of the exact copy of that value, I get a rerender. For example:
// The zustand store:
export const useStore = create<State>((set) => ({
users: [],
setUsers: (users: User[]) => set(state => ({...state, users }))
}));
// The updater component:
export default function App() {
const users = useStore((state) => state.users);
const setUsers = useStore((state) => state.setUsers);
return (
<div className="App">
<button
onClick={() =>
setUsers([
...users,
{ name: "some name", age: Math.floor(Math.random() * 100) }
])
}
>
Add User
</button>
<button onClick={() => setUsers(users)}>Refresh exact</button>
<button onClick={() => setUsers([...users])}>Refresh clone</button>
<Users />
</div>
);
}
Now in my Users
component, I read the users
state variable:
export const Users = () => {
const users = useStore((state) => state.users);
console.log("rerendered");
return (
<>
{users.map((user, i) => (
<div key={i}>{JSON.stringify(user, null, 2)}</div>
))}
</>
);
};
You'll notice that when adding a user, Users
rerenders, as expected. When setting the users value to itself (i.e. setUsers(users)
), we do not get an update. I would expect this, as the state variable has not changed. However, when updating the state variable to a clone of itself, setUsers([...users])
, the Users
component rerenders. I understand that this happens because we have broken referential equality. Since users !== [...users]
, it forces a rerender.
Codesandbox demonstrating the issue
How can I avoid this? How can I memoize the value of users
in the above code such that updating a state variable to an identical copy of itself, does not cause a rerender?