Optimizing Zustand: How to Prevent Unnecessary Re-renders in Your React App
Eray Kaya
Posted on April 5, 2023
The problem: When you create a store in Zustand and use it across components, any change in the store's state will cause the component to rerender, even if the component is not related to that particular state. For example, if you have a store like the one below and call useBearStore in your component, whenever the cats state is updated, your bear component will also rerender.
import create, { SetState } from "zustand";
interface BearStore {
bears: number;
cats: number;
incrementCats: () => void;
increase: (by: number) => void;
increment: () => void;
}
export const bearStore = (set: SetState<BearStore>) => ({
bears: 0,
cats: 0,
increase: (by: number) => {
set((state) => ({ bears: state.bears + by }));
},
increment: () => {
set((state) => ({ bears: state.bears += 1 }));
},
incrementCats: () => {
set((state) => ({ cats: state.cats += 1 }));
}
});
export const useBearStore = create<BearStore>(bearStore);
For example if you have a store like this and call useBearStore
in your component whenever cats state updated your bear component will rerender also.
The Solution: To prevent this issue, we can create a simple utility function using shallow in Zustand.
import { StoreApi, UseBoundStore } from "zustand";
import shallow from "zustand/shallow";
type GenericState = Record<string, any>;
export const createStoreWithSelectors = <T extends GenericState>(
store: UseBoundStore<StoreApi<T>>,
): (<K extends keyof T>(keys: K[]) => Pick<T, K>) => {
const useStore: <K extends keyof T>(keys: K[]) => Pick<T, K> = <K extends keyof T>(keys: K[]) => {
return store((state) => {
const x = keys.reduce((acc, cur) => {
acc[cur] = state[cur];
return acc;
}, {} as T);
return x as Pick<T, K>;
}, shallow);
};
return useStore;
};
Then we can update our initial store to use this utility function:
const bearStore = create<BearStore>(bearStore);
export const useBearStore= createStoreWithSelectors(bearStore);
Now we can use it in components like this:
const { bears, increment } = useBearStore(["bears", "increment"]);
With this change, the component won't rerender even if the cats state is updated.
Posted on April 5, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
April 5, 2023