TIL 41일차

안광의·2021년 8월 12일
0

Today I Learned

목록 보기
41/64
post-thumbnail
post-custom-banner

시작하며

오늘은 Redux 파트를 학습하기 전에 이전에 배웠던 내용을 바탕으로 React를 사용하여 쇼핑사이트의 장바구니 기능을 구현하였다. 컴포넌트로 페이지를 구분하고 Side Effect를 고려하면서 코드를 작성하고 로컬과 전역상태를 구분하는 것이 React 자체를 이해하는데 도움이 되었다.

React 장바구니 기능 구현

App.js

import React, { useState } from 'react';
import Nav from './components/Nav';
import ItemListContainer from './pages/ItemListContainer';
import './App.css';
import {
  BrowserRouter as Router,
  Switch,
  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);
  const hadleCartItems = (item) => {
    const copy = cartItems
    let isInclude = false
    copy.forEach((el) => {
      if(el.itemId === item.itemId) {
        el.quantity = el.quantity+1;
        setCartItems(copy);
        isInclude = true;
      }
    })
    if(!isInclude) {
      setCartItems([...cartItems, item])
    }
  }
  const delteCartItems = (deleteId) => {
    setCartItems(cartItems.filter((el) => el.itemId !== deleteId))
  }
  const changeCartItems = (items) => {
    setCartItems(items);
  }
  return (
    <Router>
      <Nav cartItemsNum={cartItems.length}/>
      <Switch>
        <Route exact={true} path="/">
          <ItemListContainer items={items} hadleCartItems={hadleCartItems}/>
        </Route>
        <Route path="/shoppingcart">
          <ShoppingCart cartItems={cartItems} items={items} delteCartItems={delteCartItems} changeCartItems={changeCartItems}/>
        </Route>
      </Switch>
    </Router>
  );
}
export default App;



ItemListContainer.js

import React from 'react';
import Item from '../components/Item';
function ItemListContainer({ items , hadleCartItems }) {
  const handleClick = (e, id) => {
    const cartItem = {"itemId": id, "quantity": 1}
    hadleCartItems(cartItem)
  }
  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;



Item.js

import React from 'react'
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={(e) => handleClick(e, item.id)}>장바구니 담기</button>
    </div>
  )



ShoppingCart.js

import React, { useState } from 'react'
import CartItem from '../components/CartItem'
import OrderSummary from '../components/OrderSummary'
export default function ShoppingCart({ items, cartItems, delteCartItems, changeCartItems }) {
  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) => {
    changeCartItems(cartItems.map((el) => {
      if(el.itemId === itemId) {
        return {...el, "quantity": quantity}
      }
      else return el
    }));
  }
  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId));
    delteCartItems(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>
  )
}



CartItem.js

import React from 'react'
export default function CartItem({
  item,
  checkedItems,
  handleCheckChange,
  handleQuantityChange,
  handleDelete,
  quantity
}) {
  return (
    <li className="cart-item-body">
      <input
        type="checkbox"
        className="cart-item-checkbox"
        onChange={(e) => {
          handleCheckChange(e.target.checked, item.id)
        }}
        checked={checkedItems.includes(item.id) ? true : false} >
      </input>
      <div className="cart-item-thumbnail">
        <img src={item.img} alt={item.name} />
      </div>
      <div className="cart-item-info">
        <div className="cart-item-title" data-testid={item.name}>{item.name}</div>
        <div className="cart-item-price">{item.price}</div>
      </div>
      <input
        type="number"
        min={1}
        className="cart-item-quantity"
        value={quantity}
        onChange={(e) => {
          handleQuantityChange(Number(e.target.value), item.id)
        }}>
      </input>
      <button className="cart-item-delete" onClick={() => { handleDelete(item.id) }}>삭제</button>
    </li >
  )
}



마치며

오늘 스프린트를 통해 React의 작동원리에 대해서 다시 이해할 수 있었고 내일 Redux를 사용하여 동일한 웹페이지를 구현하는 스프린트를 진행할 예정이라 미리 State와 Props로 관리할 데이터들을 파악하여 다음 스프린트 진행 시, 빠르게 파악하는데 도움이 되었다.

profile
개발자로 성장하기
post-custom-banner

0개의 댓글