hmans/statery

useStore should only rerender the component when the value in the update has actually changed

Closed this issue · 0 comments

hmans commented

Subscriptions are always passed an object containing all the changes to a store and check that object's keys if any of them are properties that are being used by the component.

The problem here is that part of that update object may not actually change the state, because the values of both the state as well as the update are the same (either equal by value, or referentially identical.)

When it comes to subscribe, this behavior is acceptable, but it does cause unnecessary rerenders of components that use useStore. This becomes particulary apparent when combining Statery with Immer:

/* A quick helper function for immer-powered state mutation. */
const mutate = <T extends State>(store: Store<T>, recipe: (state: T) => any) =>
  store.set((state) => produce(state, recipe))

Using this mutate helper function to update a state will, thanks to Immer's produce always returning a complete new state object, always set every property of the state, even if only some of them were actually modified.

Solution: in the listener function deployed by useStore, updated props should be checked if they actually received a value different from the current state before triggering a rerender.