티스토리 뷰

TIL

장바구니 list 체크박스 만들기

윤미주 2024. 3. 23. 22:03

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!)}
        >
          &times;
        </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