지영이의 개발 블로그

[React]리덕스를 적용해서 장바구니 기능 만들기! 본문

React/쇼핑몰 만들기

[React]리덕스를 적용해서 장바구니 기능 만들기!

이지영 2022. 7. 2. 20:37

하나의 장바구니 상태를 여러 컴포넌트에 걸쳐 연동시키고 싶으나 계속되는 상태 및 함수의 props 전달로 인해 복잡해지는 문제가 발생한다.이때 리덕스를 도입하면 위 문제를 깔끔히 해결할 수 있다.

 

1.리덕스를 사용해서 장바구니 보여주기

<detail.js>

      <button
            onClick={() => {
       	 dispatch(
                    addItem({
                      id: props.items[id].id,
                      name: props.items[id].title,
                      price: props.items[id].price,
                      count: 1,
                    })
                  )
                : dispatch(addCount(props.items[id].id));
   
            주문하기
          </button>

<store.js>

import { configureStore, createSlice } from "@reduxjs/toolkit";

let cart = createSlice({
  name: "cart",
  initialState: [
    { id: 11, name: "White and Black", price: 1000, count: 1 },
    { id: 12, name: "Grey Yordan", price: 4000, count: 1 },
    { id: 13, name: "oh my god", price: 2000, count: 1 },
  ],
  reducers: {
    addItem(state, action) {
      state.push(action.payload);
    },
});


export let {addItem } = cart.actions;

export default configureStore({
  reducer: {
    cart: cart.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

<cart.js>

import { Table } from "react-bootstrap";
import { useSelector, useDispatch } from "react-redux";

function Cart() {
  let state = useSelector((state) => {
    return state;
  });
  let dispatch = useDispatch();

  return (
    <Box>
      <Table striped>
        <thead>
          <tr>
            <th>상품번호</th>
            <th>상품정보</th>
            <th>판매가</th>
            <th>수량</th>
            <th>갯수</th>
          </tr>
        </thead>
        <tbody>
          {state.cart.map((a, i) => (
            <tr key={i}>
              <td>{state.cart[i].id}</td>
              <td>{state.cart[i].name}</td>
              <td>{state.cart[i].price * state.cart[i].count}</td>
            </tr>
          ))}
        </tbody>
      </Table>
    </Box>
  );
}

export default Cart;

2.장바구니 수량 증가 버튼 만들기 + 상품이 중복되는 문제 해결하기 

 

🤷‍♀️여기서 문제가 생기는데 장바구니에 추가하기 버튼을 누르면 장바구니에 상품이 추가 되는데

중복되는 상품이 계속해서 추가 되는것을 볼수있음🤷‍♀️

 

이를 해결 하기 위해 findindex 함수를 사용해주었는데  중복된 id가 있으면 그 해당 디테일 아이디 숫자가 남고, 중복된 id가 없으면 -1이 남습니다

그 dupValue값이 -1 와 같은지 비교해 일치하면 새 항목을 추가해주고, 그렇지 않으면 해당 id값에 수량 증가해주었습니다.

<detail.js>

  let state = useSelector((state) => {
    return state.cart;
  });

<button
            onClick={() => {
              let dupValue = state.findIndex((a) => {
                return a.id === items[id].id;
              });

              dupValue === -1
                ? dispatch(
                    addItem({
                      id: items[id].id,
                      name: items[id].title,
                      price: items[id].price,
                      count: 1,
                    })
                  )
                : dispatch(addCount(items[id].id));
          >
            주문하기
          </button>

 

store.js 안에 state 수정해주는 함수를 만들고 

원할 때 import 해서 사용합니다. 근데 dispatch() 로 감싸서 써야함 (useDispatch)

 

store.js

import {
  configureStore,
  createSlice,
  getDefaultMiddleware,
} from "@reduxjs/toolkit";

let cart = createSlice({
  name: "cart",
  initialState: [
    { id: 11, name: "White and Black", price: 1000, count: 1 },
    { id: 12, name: "Grey Yordan", price: 4000, count: 1 },
    { id: 13, name: "oh my god", price: 2000, count: 1 },
  ],
  reducers: {
    addCount(state, action) {
      let 번호 = state.findIndex((a) => {
        return a.id === action.payload;
      });
      state[번호].count++;
      // state[action.payload].count++
    },
    addItem(state, action) {
      state.push(action.payload);
    },
  },
});

export let { addCount, addItem } = cart.actions;

export default configureStore({
  reducer: {
    cart: cart.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

여기서 state[action.payload].count++ 으로 해줘도 되지만 정확한 기능을 위해서 (예를들면 정렬을 쓴다거나 할때를 방지해서) 조건을걸어줍니다 

let 번호 = state.findIndex((a) => {
        return a.id === action.payload;
      });

 

3.상품금액과 총금액 계산하기

갯수 버튼을 누를때마다 판매가가 증가하고 판매가를 모두 더한값을 총금액으로 표시해 줘 보도록 하겠습니다

<cart.js>

  let total = state.cart.reduce(
    (accu, cart) => accu + cart.price * cart.count,
    0
  );

array.reduce()

reduce()메서드는 배열의 각 요소에 대해 주어진 reducer 함수를 실행하고, 하나의 결과값을 반환한다. 배열 축소의 원리로 작용한다. 즉 여러개의 값이 담긴 배열이 줄어서 최종적으로 하나의 값으로 만드는 과정

arr.reduce(callback (누산기, 현재값 [, currentindex [, array]]){
//return 누적 결과의 결과 값
 [, 초기값]}

reduce 함수를 사용해서  배열의 각 요소를 순회하며 callback함수의 실행 값을 누적하여 하나의 결과값을 반환하면 됨! 초기값은 합을 구하므로 0에서 부터 시작합니다.

4.삭제버튼 만들기

redux에서 state를 업데이트 하는 함수를 만들고 실행시켜줌으로써 간단하게 해결 할 수 있었다 

store.js

import {
  configureStore,
  createSlice,
} from "@reduxjs/toolkit";

let cart = createSlice({
  name: "cart",
  initialState: [
    { id: 11, name: "White and Black", price: 1000, count: 1 },
    { id: 12, name: "Grey Yordan", price: 4000, count: 1 },
    { id: 13, name: "oh my god", price: 2000, count: 1 },
  ],
  reducers: {
    deltetItem(state, action) {
      let copy = [...state];
      let 필터 = copy.filter((a) => a.id !== action.payload);
      return 필터;
    },
  },
});

export let {  deltetItem } = cart.actions;

cart.js

  <tbody>
          {state.cart.map((a, i) => (
            <tr key={i}>
              <td>{state.cart[i].id}</td>
              <td>{state.cart[i].name}</td>
              <td>{state.cart[i].price * state.cart[i].count}</td>
              <td>{state.cart[i].count}</td>
              <td>
                <button
                  onClick={() => {
                    dispatch(addCount(state.cart[i].id));
                    //  let total =(`${state.cart[i].price * state.cart[i].count}`)
                  }}
                >
                  +
                </button>
              </td>
              <td>
                <button
                  onClick={() => {
                    dispatch(deltetItem(state.cart[i].id));
                  }}
                >
                  X
                </button>
              </td>
            </tr>
          ))}
          총금액 : {total}
        </tbody>

 

Comments