submit 핸들러로 인하여 컴포넌트 별로 각자의 역할(상태)을 나누지 못하고 최상위 컴포넌트가 모든 역할을 도맡아하고 있다.
Context API를 활용하여 상태를 분리한다.
-> 최상위 컴포넌트에서 상태를 분리하여 목표를 달성
-> 하지만 provider를 전체로 감싸게 되어 리렌더 발생
zustand를 활용하여 상태를 분리한다.
-> 상태를 분리함과 동시에 selector 함수를 활용하여 불필요한 리렌더 방지
zustand를 활용하여 상태와 handler 함수들을 모두 분리해낼 수 있었다.
export const PostProductModal: React.FC = () => {
return (
<FullScreenModalSheet>
<Title />
<Inputs />
<EditBar />
</FullScreenModalSheet>
);
};
const initialState = {
imageId: 0,
images: null,
thumbnailId: null,
title: '',
price: '',
content: '',
selectCategory: null,
isCategoryListModalOpen: false,
};
export const usePostProductModalStore = create<PostProductModalStore>()(
(set, get) => ({
...initialState,
getImageIdAndIncrement: () => {
const { imageId, incrementImageId } = get();
incrementImageId();
return imageId;
},
incrementImageId: () => set(({ imageId }) => ({ imageId: imageId + 1 })),
addImage: (image) => {
const { getImageIdAndIncrement } = get();
const id = getImageIdAndIncrement();
set(({ images }) =>
images
? { images: [...images, { ...image, id }] }
: { images: [{ ...image, id }] },
);
},
deleteImage: (id: number) => {
set(({ images }) =>
images
? { images: images.filter((image) => image.id !== id) }
: { images },
);
},
setThumbnailId: (thumbnailId) => set(() => ({ thumbnailId })),
setTitle: (title) => set(() => ({ title })),
setPrice: (price) => set(() => ({ price })),
setContent: (content) => set(() => ({ content })),
setSelectCategory: (category) => set(() => ({ selectCategory: category })),
openCategoryListModal: () => set(() => ({ isCategoryListModalOpen: true })),
closeCategoryListModal: () =>
set(() => ({ isCategoryListModalOpen: false })),
getPostProductParams: () => {
const { images, thumbnailId, title, price, content, selectCategory } =
get();
const { currentRegion } = useUserStore.getState();
const thumbnailImage = images!.find(({ id }) => id === thumbnailId)!
.imageFile!;
const imagesWithoutThumbnail = images!
.filter(({ id }) => id !== thumbnailId)
.map(({ imageFile }) => imageFile);
return {
thumbnailImage,
images: imagesWithoutThumbnail,
title,
price,
content,
region: currentRegion.addressName,
status: '판매중',
categoryId: selectCategory!.id,
categoryName: selectCategory!.name,
};
},
canSubmit: () => {
const { images, title, selectCategory, thumbnailId } = get();
return !(
title &&
selectCategory?.id &&
images?.some(({ id }) => id === thumbnailId)
);
},
reset: () => set({ ...initialState }),
}),
);