useProductDetailStore hook 생성
export default function useProductDetailStore() {
const store = container.resolve(ProductDetailStore);
return useStore(store);
}
// 초기값을 null로 잡을 수 있음
product: ProductDetail | null = null;
// null을 계속 체크해야하는 상황이나 에러를 피하기 위해 사용 ✅
product: ProductDetail = nullProductDetail;
export const nullProductDetail: ProductDetail = {
id: '',
category: { id: '', name: '' },
images: [],
name: '',
price: 0,
options: [],
description: '',
};
export default function useFetchProduct({ productId }: {
productId: string;
}): {
loading: boolean;
error: boolean;
} {
const [{ loading, error }, productDetailStore] = useProductDetailStore();
useEffect(() => {
productDetailStore.fetchProduct({ productId });
}, [productDetailStore, productId]);
return { loading, error };
}
ProductDetail.tsx
컴포넌트 구현
const Container = styled.div`
display: flex;
justify-content: space-between;
aside {
width: 38%;
}
article {
width: 60%;
}
`;
export default function ProductDetailView() {
const [{ product }] = useProductDetailStore();
return (
<Container>
<aside>
<Images images={product.images} />
</aside>
<article>
<h2>{product.name}</h2>
<AddToCartForm />
<Description value={product.description} />
</article>
</Container>
);
}
const Thumbnail = styled.img.attrs({
alt: 'Product Image',
})`
display: block;
width: 100%;
aspect-ratio: 1/1;
`;
type ImagesProps = {
images: Image[];
}
export default function Images({ images }: ImagesProps) {
const [image] = images;
if (!image) {
return null;
}
return (
<Thumbnail src={image.url} />
);
}
function key(value: string, index: number) {
return `${index}-${value}`;
}
const Container = styled.div`
li {
min-height: 1rem;
line-height: 1.4;
}
`;
type DescriptionProps = {
value: string;
}
export default function Description({ value }: DescriptionProps) {
if (!value.trim()) {
return null;
}
const lines = value.split('\n');
return (
<Container>
<ul>
{lines.map((line, index) => (
<li key={key(line, index)}>
{line}
</li>
))}
</ul>
</Container>
);
}