전체 스토어를 구독하여 불필요한 리렌더링이 많이 일어난다.
액션을 구조 분해하고 그 중 하나만 사용한다.
const useBearStore = create((set) => ({
bears: 0,
fish: 0,
// ⬇️ separate "namespace" for actions
actions: {
increasePopulation: (by) =>
set((state) => ({ bears: state.bears + by })),
eatFish: () => set((state) => ({ fish: state.fish - 1 })),
removeAllBears: () => set({ bears: 0 }),
},
}))
export const useBears = () => useBearStore((state) => state.bears)
export const useFish = () => useBearStore((state) => state.fish)
// 🎉 one selector for all our actions
export const useBearActions = () => useBearStore((state) => state.actions)
사용 팁을 적용하여 구현
import { create } from 'zustand';
import { getMyReviewAPI } from '@/api/me';
import { getRecommendRestaurantAPI } from '@/api/recommend';
import {
restaurantReviewCreate,
restaurantReviewDelete,
restaurantReviewUpdate,
} from '@/api/reviews';
import type { RestaurantType } from '@/api/types';
import type { DistrictCodeType } from '@/constants/position';
interface BearState {
selectItem: RestaurantType | null;
recommendList: RestaurantType[] | null;
myList: RestaurantType[];
selectDistrict: DistrictCodeType | null;
fetch: {
fetchRecommendList: () => Promise<RestaurantType[]>;
fetchMyList: () => Promise<RestaurantType[]>;
};
actions: {
setSelectItem: (item: RestaurantType | null) => void;
initSelectItem: () => void;
setSelectDistrict: (districtCode: DistrictCodeType | null) => void;
setRecommendList: (list: RestaurantType[] | null) => void;
setMyList: (list: RestaurantType[]) => void;
reviewCreate: (id: number, newRating: number) => Promise<void>;
reviewUpdate: (id: number, newRating: number) => Promise<void>;
reviewDelete: (id: number) => Promise<void>;
};
_reload: {
selectItemReload: () => Promise<void>;
};
}
const useRestaurantStore = create<BearState>((set, get) => ({
selectItem: null,
recommendList: null,
myList: [],
selectDistrict: null,
fetch: {
fetchRecommendList: async () => {
const districtCode = get().selectDistrict;
if (!districtCode) return [];
const recommendList = await getRecommendRestaurantAPI(districtCode);
get().actions.setRecommendList(recommendList);
return recommendList;
},
fetchMyList: async () => {
const list = await getMyReviewAPI();
get().actions.setMyList(list);
return list;
},
},
_reload: {
selectItemReload: async () => {
const districtCode = get().selectDistrict;
if (!districtCode) return;
const list = await get().fetch.fetchMyList();
const currentSelectItem = get().selectItem
? list.find((item) => item.id === get().selectItem?.id)
: null;
currentSelectItem && get().actions.setSelectItem(currentSelectItem);
},
},
actions: {
setSelectItem: (item) =>
set(() => {
console.log('set item: ', item);
return { selectItem: item };
}),
initSelectItem: () =>
set(() => {
console.log('initSelectItem: ');
return { selectItem: null };
}),
setSelectDistrict: (districtCode) => set({ selectDistrict: districtCode }),
setRecommendList: (list) => set({ recommendList: list }),
setMyList: (list) => set({ myList: list }),
reviewCreate: async (id: number, newRating: number) => {
try {
await restaurantReviewCreate(id, newRating);
get().fetch.fetchRecommendList();
get()._reload.selectItemReload();
} catch (error) {}
},
reviewUpdate: async (id: number, newRating: number) => {
try {
await restaurantReviewUpdate(id, newRating);
get()._reload.selectItemReload();
} catch (error) {}
},
reviewDelete: async (id: number) => {
try {
await restaurantReviewDelete(id);
get()._reload.selectItemReload();
} catch (error) {}
},
},
}));
export const useSelectRestaurant = () =>
useRestaurantStore((state) => state.selectItem);
export const useRecommendList = () =>
useRestaurantStore((state) => state.recommendList);
export const useSelectDistrict = () =>
useRestaurantStore((state) => state.selectDistrict);
export const useMyList = () => useRestaurantStore((state) => state.myList);
export const useRestaurantActions = () =>
useRestaurantStore((state) => state.actions);
export const useRestaurantFetch = () =>
useRestaurantStore((state) => state.fetch);