1

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?

Seth Lutske
  • 9,154
  • 5
  • 29
  • 78

0 Answers0