Jieunny์˜ ๋ธ”๋กœ๊ทธ

S3) Unit 4. [์‹ค์Šต] CMarket Hooks ๋ณธ๋ฌธ

CodeStates/Training

S3) Unit 4. [์‹ค์Šต] CMarket Hooks

Jieunny 2023. 2. 24. 09:42

๐Ÿ“ฃ  ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ตฌํ˜„ํ•˜๊ธฐ

โœ”๏ธ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€ ๋ฐ ์ƒํ’ˆ ๊ฐœ์ˆ˜ ์—…๋ฐ์ดํŠธ

1. ๋ฉ”์ธ ํ™”๋ฉด์—์„œ '์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ํ›„, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋ฉด ์ƒํ’ˆ์ด ๋‹ด๊ฒจ์žˆ์–ด์•ผ ํ•œ๋‹ค.

โžก๏ธ ItemListContainer.js

import React from 'react';
import Item from '../components/Item';

function ItemListContainer({ items, cartItems, setCartItems }) {
  const handleClick = (e, id) => { 
    let addItem = {
      itemId: id,
      quantity: 1,
    }

    for(let i=0; i<cartItems.length; i++){
      if(id === cartItems[i].itemId) cartItems[i].quantity++; // ์ด๋ฏธ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์žˆ์œผ๋ฉด ๊ฐœ์ˆ˜ ์ฆ๊ฐ€
      else setCartItems([...cartItems, addItem]); // ์—†์œผ๋ฉด ๊ทธ ์•„์ดํ…œ ์ž์ฒด๋ฅผ ๋‹ด์•„์ฃผ๊ธฐ
    }
  }
  
  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">์“ธ๋ชจ์—†๋Š” ์„ ๋ฌผ ๋ชจ์Œ</div>
        {items.map((item, idx) => <Item item={item} key={idx} handleClick={handleClick} />)}
      </div>
    </div>
  );
}

export default ItemListContainer;

โžฐ App.js์—์„œ items, cartItems, setCartItems state๋ฅผ props๋กœ ๋ฐ›์•„์˜จ๋‹ค.

โžฐ ๋ฐ›์•„์˜จ id์™€ ์ˆ˜๋Ÿ‰์„ ์†์„ฑ์œผ๋กœ ๊ฐ€์ง€๋Š” ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€ ํ•  ์ƒˆ ๊ฐ์ฒด๋ฅผ ์„ ์–ธํ•œ๋‹ค.

โžฐ cartItems ์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ ๋ชฉ๋ก์„ ์˜๋ฏธํ•˜๋ฉฐ, ์ด ๋ฐฐ์—ด์„ for๋ฌธ์œผ๋กœ ๋Œ๋ฉด์„œ ํ™•์ธํ•œ๋‹ค.

โžฐ ๋งŒ์•ฝ id๊ฐ€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์žˆ๋Š” ์•„์ดํ…œ id์™€ ๊ฐ™๋‹ค๋ฉด ์ด๋ฏธ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์žˆ๋Š” ์•„์ดํ…œ์ด๋ฏ€๋กœ ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ์ˆ˜๋Ÿ‰๋งŒ ์ฆ๊ฐ€์‹œ์ผœ์ค€๋‹ค.

โžฐ id๊ฐ€ ์—†๋‹ค๋ฉด, ๊ทธ ์•„์ดํ…œ์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์—†๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์•„๊นŒ ์„ ์–ธํ•œ ์ƒˆ ๊ฐ์ฒด์™€ ์›๋ž˜ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ธด ์•„์ดํ…œ์„ ์ƒˆ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด์„œ cartItems๋ฅผ ๊ฐฑ์‹ ํ•ด์ค€๋‹ค.

 

2. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€์—์„œ ๊ฐ ์•„์ดํ…œ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

โžก๏ธ ShoppingCart.js

import React, { useState } from 'react'
import CartItem from '../components/CartItem'
import OrderSummary from '../components/OrderSummary'

export default function ShoppingCart({ items, cartItems, setCartItems }) {
  const [checkedItems, setCheckedItems] = useState(cartItems.map((el) => el.itemId))

  const handleCheckChange = (checked, id) => {
    if (checked) {
      setCheckedItems([...checkedItems, id]);
    }
    else {
      setCheckedItems(checkedItems.filter((el) => el !== id));
    }
  };

  const handleAllCheck = (checked) => {
    if (checked) {
      setCheckedItems(cartItems.map((el) => el.itemId))
    }
    else {
      setCheckedItems([]);
    }
  };

  const handleQuantityChange = (quantity, itemId) => {
    let newCartItems = [...cartItems];
    for(let i=0; i<newCartItems.length; i++){
      if(newCartItems[i].itemId === itemId){
        newCartItems[i].quantity = quantity;
        //console.log(quantity);
        setCartItems(newCartItems);
      }
    }
  }

  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId));
    setCartItems(cartItems.filter((el) => el.itemId !== itemId));
  }

  const getTotal = () => {
    let cartIdArr = cartItems.map((el) => el.itemId)
    let total = {
      price: 0,
      quantity: 0,
    }
    for (let i = 0; i < cartIdArr.length; i++) {
      if (checkedItems.indexOf(cartIdArr[i]) > -1) {
        let quantity = cartItems[i].quantity
        let price = items.filter((el) => el.id === cartItems[i].itemId)[0].price

        total.price = total.price + quantity * price
        total.quantity = total.quantity + quantity
      }
    }
    return total
  }

  const renderItems = items.filter((el) => cartItems.map((el) => el.itemId).indexOf(el.id) > -1)
  const total = getTotal()

  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">์žฅ๋ฐ”๊ตฌ๋‹ˆ</div>
        <span id="shopping-cart-select-all">
          <input
            type="checkbox"
            checked={
              checkedItems.length === cartItems.length ? true : false
            }
            onChange={(e) => handleAllCheck(e.target.checked)} >
          </input>
          <label >์ „์ฒด์„ ํƒ</label>
        </span>
        <div id="shopping-cart-container">
          {!cartItems.length ? (
            <div id="item-list-text">
              ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์•„์ดํ…œ์ด ์—†์Šต๋‹ˆ๋‹ค.
            </div>
          ) : (
              <div id="cart-item-list">
                {renderItems.map((item, idx) => {
                  const quantity = cartItems.filter(el => el.itemId === item.id)[0].quantity
                  return <CartItem
                    key={idx}
                    handleCheckChange={handleCheckChange}
                    handleQuantityChange={handleQuantityChange}
                    handleDelete={handleDelete}
                    item={item}
                    checkedItems={checkedItems}
                    quantity={quantity}
                  />
                })}
              </div>
            )}
          <OrderSummary total={total.price} totalQty={total.quantity} />
        </div>
      </div >
    </div>
  )
}

โžฐ ์ˆ˜๋Ÿ‰ ๋ณ€๊ฒฝ์€ handleQuantityChange ํ•จ์ˆ˜์—์„œ ๊ตฌํ˜„ํ•œ๋‹ค.

โžฐ state๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด์˜ cartItems๋ฅผ ๊นŠ์€ ๋ณต์‚ฌ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

โžฐ ๋ณต์‚ฌํ•œ ๋ฐฐ์—ด์„ ๋Œ๋ฉด์„œ, ํด๋ฆญํ•œ id์™€ ๊ฐ™์€ id์˜ ์•„์ดํ…œ์„ ๋งŒ๋‚˜๋ฉด ๊ทธ ์•„์ดํ…œ์˜ quantity๋ฅผ ++ ํ•ด์ค€๋‹ค.

โžฐ cartItems๋ฅผ ๋ณต์‚ฌํ•œ ์ƒˆ ๋ฐฐ์—ด๋กœ ๊ฐฑ์‹ ํ•ด์ค€๋‹ค.

 

3. ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ”์— ์ƒํ’ˆ ๊ฐœ์ˆ˜๊ฐ€ ์ฆ‰์‹œ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•œ๋‹ค.

โžก๏ธ Nav.js

import React from 'react';
import { Link } from 'react-router-dom';

function Nav({cartItems}) {

  return (
    <div id="nav-body">
      <span id="title">
        <img id="logo" src="../logo.png" alt="logo" />
        <span id="name">CMarket</span>
      </span>
      <div id="menu">
        <Link to="/">์ƒํ’ˆ๋ฆฌ์ŠคํŠธ</Link>
        <Link to="/shoppingcart">
          ์žฅ๋ฐ”๊ตฌ๋‹ˆ<span id="nav-item-counter">{cartItems.length}</span>
        </Link>
      </div>
    </div>
  );
}

export default Nav;

โžฐ props๋กœ ๋ฐ›์•„์˜จ cartItems์˜ ๊ธธ์ด๋ฅผ ์ถœ๋ ฅํ•ด์ค€๋‹ค.

 

โœ”๏ธ ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋กœ๋ถ€ํ„ฐ ์ œ๊ฑฐ

1. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€์—์„œ '์‚ญ์ œ' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ•ด๋‹น ์ƒํ’ˆ์ด ๋ชฉ๋ก์—์„œ ์‚ญ์ œ๋˜์–ด์•ผ ํ•œ๋‹ค.

โžก๏ธ ShoppingCart.js

import React, { useState } from 'react'
import CartItem from '../components/CartItem'
import OrderSummary from '../components/OrderSummary'

export default function ShoppingCart({ items, cartItems, setCartItems }) {
  const [checkedItems, setCheckedItems] = useState(cartItems.map((el) => el.itemId))

  const handleCheckChange = (checked, id) => {
    if (checked) {
      setCheckedItems([...checkedItems, id]);
    }
    else {
      setCheckedItems(checkedItems.filter((el) => el !== id));
    }
  };

  const handleAllCheck = (checked) => {
    if (checked) {
      setCheckedItems(cartItems.map((el) => el.itemId))
    }
    else {
      setCheckedItems([]);
    }
  };

  const handleQuantityChange = (quantity, itemId) => {
    let newCartItems = [...cartItems];
    for(let i=0; i<newCartItems.length; i++){
      if(newCartItems[i].itemId === itemId){
        newCartItems[i].quantity = quantity;
        //console.log(quantity);
        setCartItems(newCartItems);
      }
    }
  }

  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId));
    setCartItems(cartItems.filter((el) => el.itemId !== itemId));
  }

  const getTotal = () => {
    let cartIdArr = cartItems.map((el) => el.itemId)
    let total = {
      price: 0,
      quantity: 0,
    }
    for (let i = 0; i < cartIdArr.length; i++) {
      if (checkedItems.indexOf(cartIdArr[i]) > -1) {
        let quantity = cartItems[i].quantity
        let price = items.filter((el) => el.id === cartItems[i].itemId)[0].price

        total.price = total.price + quantity * price
        total.quantity = total.quantity + quantity
      }
    }
    return total
  }

  const renderItems = items.filter((el) => cartItems.map((el) => el.itemId).indexOf(el.id) > -1)
  const total = getTotal()

  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">์žฅ๋ฐ”๊ตฌ๋‹ˆ</div>
        <span id="shopping-cart-select-all">
          <input
            type="checkbox"
            checked={
              checkedItems.length === cartItems.length ? true : false
            }
            onChange={(e) => handleAllCheck(e.target.checked)} >
          </input>
          <label >์ „์ฒด์„ ํƒ</label>
        </span>
        <div id="shopping-cart-container">
          {!cartItems.length ? (
            <div id="item-list-text">
              ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์•„์ดํ…œ์ด ์—†์Šต๋‹ˆ๋‹ค.
            </div>
          ) : (
              <div id="cart-item-list">
                {renderItems.map((item, idx) => {
                  const quantity = cartItems.filter(el => el.itemId === item.id)[0].quantity
                  return <CartItem
                    key={idx}
                    handleCheckChange={handleCheckChange}
                    handleQuantityChange={handleQuantityChange}
                    handleDelete={handleDelete}
                    item={item}
                    checkedItems={checkedItems}
                    quantity={quantity}
                  />
                })}
              </div>
            )}
          <OrderSummary total={total.price} totalQty={total.quantity} />
        </div>
      </div >
    </div>
  )
}

โžฐ ์•„์ดํ…œ ์‚ญ์ œ๋Š” handleDelete ํ•จ์ˆ˜์—์„œ ๊ตฌํ˜„ํ•œ๋‹ค.

โžฐ cartItems์— filter๋ฅผ ์‹คํ–‰ํ•ด์„œ, ํด๋ฆญํ•œ id์™€ id๊ฐ€ ๊ฐ™์€ ์•„์ดํ…œ์€ ์ œ์™ธํ•ด์ค€๋‹ค.

 

2. ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ”์— ์ƒํ’ˆ ๊ฐœ์ˆ˜๊ฐ€ ์ฆ‰์‹œ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•œ๋‹ค.

โžฐ ์œ„์˜ 3๋ฒˆ๊ณผ ๊ฐ™๋‹ค.

 

๐Ÿ“ฃ  ์‹œ์—ฐ ํ™”๋ฉด