[React] Cmarket Hooks

유아현·2022년 12월 27일
0

React

목록 보기
13/28
post-thumbnail

📘 [TODO] 쇼핑몰 애플리케이션의 주요 기능 구현

  • [장바구니 담기] 버튼을 이용해 장바구니에 해당 상품이 추가
  • 장바구니 내 [삭제] 버튼을 이용해 장바구니의 상품이 제거
  • 장바구니 내에서 각 아이템 개수를 변경
  • 장바구니의 상품 개수의 변동이 생길 때마다, 상단 내비게이션 바에 상품 개수가 업데이트

✨ Intro | 컴포넌트 구조와 데이터 흐름 파악을 위한 그림 그리기


🖥️ App.js

import React, { useState } from 'react';
import Nav from './components/Nav';
import ItemListContainer from './pages/ItemListContainer';
import './App.css';
import './variables.css';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ShoppingCart from './pages/ShoppingCart';
import { initialState } from './assets/state';

function App() {
  const [items, setItems] = useState(initialState.items);
  const [cartItems, setCartItems] = useState(initialState.cartItems);

  return (
    <Router>
      <Nav cartcnt={cartItems.length} />
      <Routes>
        {/* ItemListContainer의 items은 useState의 현재 itmes들을 나타내고 
        useState의 items는 initialState.items를 가지고 오고 있다.
        initialState{
          "items": [              ItemListContainer에 전달 => 상품에 대한 정보
            {
              "id": 1,
              "name": "노른자 분리기",
              "img": "../images/egg.png",
              "price": 9900}
              ],
          "cartItems":[           cartItems에 전달 => 장바구니에 담은 아이템 Id와 수량
            {
              "itemId": 1,
              "quantity": 1
            }
          ]             
              } */}
        <Route
          path="/"
          element={
            <ItemListContainer
              items={items}
              cartItems={cartItems}
              setCartItems={setCartItems}
            />
          }
        />
        <Route
          path="/shoppingcart"
          element={
            <ShoppingCart
              cartItems={cartItems}
              items={items}
              setCartItems={setCartItems}
            />
          }
        />
      </Routes>
      <img
        id="logo_foot"
        src={`${process.env.PUBLIC_URL}/codestates-logo.png`}
        alt="logo_foot"
      />
    </Router>
  );
}

export default App;

🖥️ ItemListContainer.js

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

//! items로 가져온 것은 배열 형태임
{
  /*"items": [                      => 상품에 대한 정보
    {
      "id": 1,
      "name": "노른자 분리기",
      "img": "../images/egg.png",
      "price": 9900}
      ...
    }
  ], */
}

function ItemListContainer({ items, cartItems, setCartItems }) {
  const handleClick = (itemId) => {
    // 기존 장바구니의 아이템 아이디가 새로 클릭한 아이템 아이디와 같은 경우는
    // 이미 장바구니의 아이템이 존재한다는 것이므로 존재하지 않을 때만 추가해 준다.

    let itemIdx = cartItems.findIndex((ele) => ele.itemId === itemId);
    if (!cartItems.filter((ele) => ele.itemId === itemId)[0]) {
      setCartItems([
        ...cartItems,
        {
          itemId: itemId,
          quantity: 1,
        },
      ]);
    } else {
      //이미 있는 경우, 해당 인덱스 증감시켜주기
      cartItems[itemIdx].quantity += 1;
      setCartItems([...cartItems]);
    }
  };
  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">쓸모없는 선물 모음</div>
        {/* 배열 items를 Props로 전달
        Item에서 item은 ites 요소 하나하나이며, Key는 인덱스 번호 */}
        {items.map((item, idx) => (
          <Item item={item} key={idx} handleClick={handleClick} />
        ))}
      </div>
    </div>
  );
}

export default ItemListContainer;

🖥️ Item.js

import React from 'react';

//! 요소 하나하나씩 받고 있음
{
  /*"items": [                      => 상품에 대한 정보
    {
      "id": 1,
      "name": "노른자 분리기",
      "img": "../images/egg.png",
      "price": 9900}
      ...
    }
  ], */
}

export default function Item({ item, handleClick }) {
  return (
    <div key={item.id} className="item">
      <img className="item-img" src={item.img} alt={item.name}></img>
      <span className="item-name">{item.name}</span>
      <span className="item-price">{item.price}</span>
      <button className="item-button" onClick={() => handleClick(item.id)}>
        장바구니 담기
      </button>
    </div>
  );
}

🖥️ ShoppingCart.js

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

//! items로 가져온 것은 배열 형태임
{
  /*"items": [                      => 상품에 대한 정보
    {
      "id": 1,
      "name": "노른자 분리기",
      "img": "../images/egg.png",
      "price": 9900}
      ...
    }
  ], */
}

//! cartItems로 가져온 것은 배열 형태임
{
  /*
  "cartItems":[           cartItems에 전달 => 장바구니에 담은 아이템 Id와 수량
    {
      "itemId": 1,
      "quantity": 1
    }
  ]   
*/
}

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) => {
    setCartItems(
      cartItems.map((ele) => {
        if (ele.itemId === itemId) {
          return {
            itemId: itemId,
            quantity: quantity,
          };
        } else {
          return ele;
        }
      })
    );
  };

  //! [삭제] 버튼 누르면 장바구니의 상품이 제거
  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId));
    // 기존 카트에 있는 아이템의 itemId와 삭제버튼을 누른 itemId가 같지 않은 것만 뽑아오기
    setCartItems(cartItems.filter((ele) => ele.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>
  );
}

🖥️ Nav.js

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

//! items로 가져온 것은 배열 형태임
{
  /*"items": [                      => 상품에 대한 정보
    {
      "id": 1,
      "name": "노른자 분리기",
      "img": "../images/egg.png",
      "price": 9900}
      ...
    }
  ], */
}

function ItemListContainer({ items, cartItems, setCartItems }) {
  const handleClick = (itemId) => {
    // 기존 장바구니의 아이템 아이디가 새로 클릭한 아이템 아이디와 같은 경우는
    // 이미 장바구니의 아이템이 존재한다는 것이므로 존재하지 않을 때만 추가해 준다.

    let itemIdx = cartItems.findIndex((ele) => ele.itemId === itemId);
    if (!cartItems.filter((ele) => ele.itemId === itemId)[0]) {
      setCartItems([
        ...cartItems,
        {
          itemId: itemId,
          quantity: 1,
        },
      ]);
    } else {
      //이미 있는 경우, 해당 인덱스 증감시켜주기
      cartItems[itemIdx].quantity += 1;
      setCartItems([...cartItems]);
    }
  };
  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">쓸모없는 선물 모음</div>
        {/* 배열 items를 Props로 전달
        Item에서 item은 ites 요소 하나하나이며, Key는 인덱스 번호 */}
        {items.map((item, idx) => (
          <Item item={item} key={idx} handleClick={handleClick} />
        ))}
      </div>
    </div>
  );
}

export default ItemListContainer;

0개의 댓글