티스토리 뷰
next.js / typeScript 사용
1. 체크된 상품의 productId 저장
const [checkItem, setCheckItem] = useState<string[]>([]);
2. 체크박스의 체크상태를 관리 (초기에 모든 체크박스 상태는 ture)
const [isChecked, setIsChecked] = useState<boolean>(true);
3. 체크박스의 상태가 변경될 때 호출되는 함수
const handleCheckedBoxChange = (e: React.ChangeEvent<HTMLInputElement>) => { setIsChecked(e.target.checked);
handleSingleClick({ checked: e.target.checked, productId: productId! }); };
e.target.value를 통해 현재 체크된 박스의 체크상태를 가져오고,
setIsChecked를 이용해 isChecked 상태를 저장
이후 handleSingleClick 함수를 호출해 현재 상품의 체크상태와 체크된 productId를 함께전달.
4.상품의 체크상태가 변경될 때 호출되는 함수
const handleSingleClick = ({ checked, productId }: CheckProps) => {
if (checked) {
setCheckItem((prev) => [...prev, productId]);
} else {
setCheckItem(checkItem.filter((item) => item! == productId));
}
};
checked: 현재 상품의 체크상태
상품이 체크된 경우 현재 상품의 productId를 checkItem배열에 추가(이전상태prev를 이용해 새로운 상태 결정)
상품의 체크가 해제된 경우 checkItem 배열에서 해제된 상품의 productId를 제거 (filter를 이용해 일치하지 않는 항목 제거)
장바구니 리스트 전체코드
'use client';
import { FaPlusCircle } from 'react-icons/fa';
import { FaMinusCircle } from 'react-icons/fa';
import { ProductType } from '@/types/product-type';
import { useDeleteCartData, useUpdateCartData } from '@/utill/hooks/cart/useCart';
import { userState } from '@/store/modules/user';
import { useAppSelector } from '@/utill/hooks/redux/useRedux';
import { useState } from 'react';
import { set } from 'firebase/database';
interface ProductProps {
product: ProductType;
}
interface CheckProps {
checked: boolean;
productId: string;
}
export default function CartItem({ product }: ProductProps) {
const { productId, image, title, price, quantity } = product;
const { deleteCartMutate } = useDeleteCartData();
const { updateCartMutate } = useUpdateCartData();
const productPrice = price * quantity;
const userId = useAppSelector(userState);
const [checkItem, setCheckItem] = useState<string[]>([]);
const [isChecked, setIsChecked] = useState<boolean>(true);
const handleCheckedBoxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIsChecked(e.target.checked);
handleSingleClick({ checked: e.target.checked, productId: productId! });
};
const handleSingleClick = ({ checked, productId }: CheckProps) => {
if (checked) {
setCheckItem((prev) => [...prev, productId]);
} else {
setCheckItem(checkItem.filter((item) => item! == productId));
}
};
const handleMinus = async (userId: string, productId: string, quantity: number) => {
if (quantity < 2) return;
updateCartMutate({ userId, productId, quantity: quantity - 1 });
};
const handlePlus = async (userId: string, productId: string, quantity: number) => {
updateCartMutate({ userId, productId, quantity: quantity + 1 });
};
const handleDelete = async (productId: string) => {
deleteCartMutate({ productId, userId });
};
return (
<li
key={productId}
className="flex justify-between items-center space-x-2 border-b-2 border-[#B4B4B8]-500 p-3 w-full"
>
<label>
<div className=" flex items-center gap-10 ">
<input type="checkbox" checked={isChecked} onChange={handleCheckedBoxChange} className=" w-4 h-4" />
<img src={image} alt={title} className="w-60 h-60" />
<h3 className=" text-xl">{title}</h3>
</div>
</label>
<div className="relative">
<button //
className="text-3xl absolute right-0 -top-24 text-[#ccc]"
onClick={() => handleDelete(productId!)}
>
×
</button>
<div className="flex gap-10 items-center text-2xl transition-all">
<FaMinusCircle
className={`transition-all cursor-pointer ${
quantity <= 1 ? 'text-gray-300 cursor-not-allowed' : 'hover:text-black'
}`}
onClick={() => handleMinus(userId, productId!, quantity)}
style={{ pointerEvents: quantity <= 1 ? 'none' : 'auto' }}
/>
<p>{quantity}</p>
<FaPlusCircle
className="transition-all cursor-pointer hover:"
onClick={() => handlePlus(userId, productId!, quantity)}
/>
</div>
<div className="absolute right-5 top-20 text-xl whitespace-nowrap">
<span className="text-2xl"> {new Intl.NumberFormat('ko-KR').format(productPrice)}</span> 원
</div>
</div>
</li>
);
}
장바구니 페이지 전체코드
'use client';
import Header from '@/components/common/Header';
import { TiShoppingCart } from 'react-icons/ti';
import CartItem from '@/components/cart/CartItem';
import { useReadCartData } from '@/utill/hooks/cart/useCart';
import { userState } from '@/store/modules/user';
import { useAppSelector } from '@/utill/hooks/redux/useRedux';
export default function CartPage() {
const userId = useAppSelector(userState);
const { data: carts } = useReadCartData(userId);
const formatter = new Intl.NumberFormat('ko-KR');
const deliveryprice = 3000;
const hasProducts = carts && carts.length > 0;
const total = carts && carts.reduce((prev, current) => prev + current.price * current.quantity, 0);
const totalPrice = formatter.format(total! + deliveryprice);
return (
<div className="w-full h-lvh">
<Header />
<article className="flex flex-col w-full items-center h-full pt-40">
<div className="w-4/5 h-4/5 items-center">
<section className="flex flex-col items-center bg-10">
<h1 className="flex flex-row text-5xl mb-10">
<TiShoppingCart className=" text-6xl -translate-y-3 mr-2" />
장바구니
</h1>
</section>
<div className="flex flex-row w-full mt-30 mb-5 text-2xl">
<button>전체 삭제하기</button>
</div>
<section className="flex flex-row justify-between w-full h-full ">
<article className="flex flex-col items-center border h-full w-3/4 overflow-auto">
<div className="p-10 w-full">
{!hasProducts && <p>장바구니에 상품이 없습니다. 열심히 쇼핑해주세요!</p>}
{hasProducts && (
<>
<ul>{carts && carts.map((product) => <CartItem key={product.productId} product={product} />)}</ul>
</>
)}
</div>
</article>
<article className="flex flex-col w-1/4 m-10 text-xl border p-9 h-52">
<div className="flex flex-row justify-between">
<p>상품금액</p>
{!hasProducts ? <p> 원</p> : <p>{formatter.format(total!)}원</p>}
</div>
<div className="flex flex-row justify-between mt-3 text-[#ccc]">
<p>배송비</p>
{!hasProducts ? <p> 원</p> : <p> 3,000원</p>}
</div>
<div className="flex flex-row justify-between text-2xl mt-5 border-t-2 border-[#B4B4B8]-500">
<p className="mt-5">결제예정금액</p>
<div className="mt-5">
{!hasProducts ? <p>원</p> : <span className="text-3xl">{totalPrice}원</span>}
</div>
</div>
</article>
</section>
</div>
</article>
</div>
);
}
'TIL' 카테고리의 다른 글
초기 셋팅 코드 및 설치 명령어 모음집!! (0) | 2024.05.19 |
---|---|
Next.js Error Handling (0) | 2024.04.25 |
TS(TypeScript) 파일 만드는 방법 (0) | 2024.03.04 |
CSS :hover :active :focus 차이 (0) | 2024.03.03 |
loadingbar 만들기! (0) | 2024.02.29 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- git cache
- readme 역할
- axios instance 작성하기
- axios CRUD
- readme 이미지 추가 방법
- Warning: validateDOMNesting(...): <li> cannot appear as a descendant of <li>
- styled component GlobalStyle 사용방법
- readme작성해보기
- nextjs 토큰 만료처리하기
- Warning: A component is changing an uncontrolled input to be controlled.
- readme 작성해야 하는 이유
- 영화별점만들기
- styled component 사용방법
- 별점만들기
- styled component 설치방법
- simple Icon 사용방법
- axiosinstance 사용 시 토큰 사용 법
- 영화 별점
- axios 사용하기
- Warning: Each child in a list should have a unique "key" prop.
- 유효성검사 css
- Fetch와 Axios 의 장단점
- 유효성검사
- 에러모음집
- readme 작성 방법
- 별점 색채우기
- styled component 조건부 사용방법
- axios 설치하기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
글 보관함