Lecture/์ฝ”๋“œ์Šคํ…Œ์ด์ธ 

[SEB FE] Section3 unit 4 (3) Cmarket Redux

Dong _ hwa 2023. 5. 18. 08:46
  • ์•„์ดํ…œ ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€(ItemListContainer)์™€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€(ShoppingCart) ์ด ๋‘ ํŽ˜์ด์ง€๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • Store์˜ initial state์—๋Š” ์ „์ฒด ์•„์ดํ…œ ๋ชฉ๋ก(items), ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก(cartItems)์ด ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ ItemListContainer, ShoppingCart ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ ๋ฐ components ํด๋”์˜ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋“ค์—์„œ Store(state)์— ์ ‘๊ทผํ•ด ๋ณด์„ธ์š”.(Redux์—์„œ ์ œ๊ณตํ•˜๋Š” hooks, useDispatch, useSelector๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.)






โ˜‘๏ธ Action

actions / index.js

export const addToCart = (itemId) => {
  return {
    type: ADD_TO_CART,
    payload: {
      quantity: 1,
      itemId
    }
  }
}

export const removeFromCart = (itemId) => {
  return {
    type : REMOVE_FROM_CART,
    payload: {
      itemId
    }
  }
}

export const setQuantity = (itemId, quantity) => {
  return {
    type : SET_QUANTITY,
    payload : {
      itemId,
      quantity
    }
  }
}





โ˜‘๏ธ Dispatch

pages / ItemListContainer.js

import React from 'react';
import { addToCart, notify } from '../actions/index';
import { useSelector, useDispatch } from 'react-redux';
import Item from '../components/Item';

function ItemListContainer() {
  const state = useSelector(state => state.itemReducer);
  const { items, cartItems } = state;
  const dispatch = useDispatch();

  const handleClick = (item) => {
    if (!cartItems.map((el) => el.itemId).includes(item.id)) {
      //TODO: dispatch ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์•„์ดํ…œ ์ถ”๊ฐ€์— ๋Œ€ํ•œ ์•ก์…˜์„ ์ „๋‹ฌํ•˜์„ธ์š”.
      dispatch(addToCart(item.id))
      dispatch(notify(`์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ${item.name}์ด(๊ฐ€) ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`))
    }
    else {
      dispatch(notify('์ด๋ฏธ ์ถ”๊ฐ€๋œ ์ƒํ’ˆ์ž…๋‹ˆ๋‹ค.'))
    }
  }

  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(item)
        }} />)}
      </div>
    </div>
  );
}

export default ItemListContainer;




pages / ShoppingCart.js

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { removeFromCart, setQuantity } from '../actions';
import CartItem from '../components/CartItem'
import OrderSummary from '../components/OrderSummary'

export default function ShoppingCart() {

  const state = useSelector(state => state.itemReducer);
  const { cartItems, items } = state
  const dispatch = useDispatch();
  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) => {
    //TODO: dispatch ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์•ก์…˜์„ ์ „๋‹ฌํ•˜์„ธ์š”.
    dispatch(setQuantity(itemId, quantity))
  }

  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId))
     //TODO: dispatch ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์•ก์…˜์„ ์ „๋‹ฌํ•˜์„ธ์š”.
    dispatch(removeFromCart(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
  }





โ˜‘๏ธ Reducer

reducers / itemReducer

import { REMOVE_FROM_CART, ADD_TO_CART, SET_QUANTITY } from "../actions/index";
import { initialState } from "./initialState";

const itemReducer = (state = initialState, action) => {

  switch (action.type) {
    case ADD_TO_CART:
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
      })

    case REMOVE_FROM_CART:
      let list = state.cartItems.filter(el => el.itemId !== action.payload.itemId)
      return Object.assign({}, state, {
        cartItems: list
      })

	
    case SET_QUANTITY:
      let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
      let arr = state.cartItems.map ((el, index) => {
        return  idx === index ? action.payload : el
      }) 
      return Object.assign({}, state, {
        cartItems: arr
      })


    default:
      return state;
  }
}

export default itemReducer;





์–ด๋ ค์› ๋˜ ์  ๐Ÿงโ‰๏ธ

case SET_QUANTITY:
      let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
      let arr = state.cartItems.map ((el, index) => {
        return  idx === index ? action.payload : el
      }) 
      return Object.assign({}, state, {
        cartItems: arr
      })

๋‚ด๊ฐ€ ์–ด์ฐŒ์ €์ฐŒ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ

case SET_QUANTITY:
      let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
      return Object.assign({}, state, {
        cartItems: [...state.cartItems.slice(0, idx), action.payload,
        ...state.cartItems.slice(idx + 1)]
      });

์ด๊ฒŒ ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ์ด๊ณ ,

return {
        ...state,
        cartItems: [...state.cartItems.slice(0, idx), action.payload, ...state.cartItems.slice(idx + 1)]
      }

์ธํ„ฐ๋„ท์—๋Š” Object.assign ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ฝ”๋“œ๊ฐ€ ์ œ์ผ ๋งŽ์ด ๋ณด์ธ๋‹ค.
๋‚ด๊ฐ€ ์–ด์ฐŒ์ €์ฐŒ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋Š” ๋ˆˆ์—๋ณด์ด๋Š” ์ง๊ด€์ ์ธ ์ฝ”๋“œ๋ผ์„œ, ์ดํ•ด๊ฐ€ ๋˜๋Š”๋ฐ
๋‚˜๋งŒ ์ด ๋ ˆํผ๋Ÿฐ์Šค๋“ค์ด ์ดํ•ด๊ฐ€ ์•ˆ๊ฐ€๋Š” ๊ฑด์ง€ ใ… _ใ…  ๋จธ๋ฆฌ๊ฐ€ ์•ˆ ๋Œ์•„๊ฐ„๋‹ค...