Zustand 스토어의 각 상태 필드에 대한 셀렉터를 자동으로 생성하는 팩토리 함수입니다.
store.use.field() 형태로 개별 필드만 구독하여 불필요한 리렌더링을 방지합니다.구현
import type { StoreApi, UseBoundStore } from 'zustand';
type WithSelectors<S> = S extends { getState: () => infer T } ? S & { use: { [K in keyof T]: () => T[K] } } : never;
export const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(_store: S) => {
const store = _store as WithSelectors<typeof _store>;
store.use = {};
for (const k of Object.keys(store.getState())) {
(store.use as Record<string, () => unknown>)[k] = () => store(s => s[k as keyof typeof s]);
}
return store;
};
사용법
1. 스토어 생성
import { create } from 'zustand';
import { createSelectors } from './createSelectors';
type StoreState = {
fruit: 'apple' | 'banana';
drink: 'water' | 'juice';
};
type StoreActions = {
setFruit: (fruit: StoreState['fruit']) => void;
setDrink: (drink: StoreState['drink']) => void;
};
type Store = StoreState & StoreActions;
const initState: StoreState = {
fruit: 'apple',
drink: 'water',
};
const store = create<Store>()(set => ({
...initState,
setFruit: fruit => set({ fruit }),
setDrink: drink => set({ drink }),
}));
export const useFruitStore = createSelectors(store);
2. 컴포넌트에서 셀렉터 사용
function Component() {
const fruit = useFruitStore.use.fruit();
const setFruit = useFruitStore.use.setFruit();
const setDrink = useFruitStore.use.setDrink();
// fruit 값이 변경될 때만 리렌더링
useEffect(() => {
setFruit('banana');
}, []);
// drink 값이 변경되어도 리렌더링되지 않음
useEffect(() => {
setDrink('juice');
}, [fruit]);
return <span>{fruit}</span>; // apple → banana
}

