Redux 프로젝트 - 상품 목록 추가 / 삭제하기 (2)

코딩하는 남자·2022년 5월 8일
0

React 정리

목록 보기
4/8
post-thumbnail

본 포스트는 Udemy 의 리액트 강좌를 정리한 글입니다.

강의 링크

상품 목록 업데이트 기능

우선 Firebase 에 업데이트하는 기능을 제외하고 완성했다.
( Firebase 구현은 다음 포스트에서 )

완성된 모습


📌 createSlice로 상품 목록 상태 구현하기

먼저 계속 변하는 상품 목록 정보를 관리할 상태가 필요했다.
나는 이것을 Redux로 구현했다.

product-slice.js

import { createSlice } from '@reduxjs/toolkit';
import randomString from '../util/randomString';

const initialState = { items: [] }; // 처음에는 빈 배열로 시작

const productSlice = createSlice({
  name: 'product',
  initialState, // 처음의 상태를 정의

  // reducers -> 상태를 업데이트 해줄 함수 모음집
  reducers: {
    // 상품 목록에 상품을 추가하는 함수
    addProduct(state, action) {
      const newItem = action.payload;
      const randomId = randomString(10); // 새로운 상품의 ID는 랜덤 스트링으로 생성한다.
      state.items.push({
        id: randomId + newItem.title,
        price: newItem.price,
        title: newItem.title,
        description: newItem.description,
      });
    },

    // 상품 목록에서 상품을 삭제하는 함수 (삭제할 상품의 ID를 매개변수로 받아옴)
    removeProduct(state, action) {
      const id = action.payload;
      state.items = state.items.filter((item) => item.id !== id);
    },
  },
});

// 이걸 이용해서 함수명 자동완성을 할 수 있다.
export const productActions = productSlice.actions; 

export default productSlice;

redux-toolkitcreateSlice 함수를 사용했다.

Redux 의 상태는 reducers 에서 정의된 함수를 사용해서 변경할 수 있다.
위의 함수들은 state(상태)를 직접 변경하는 것처럼 보이지만 실제로는 createSlice 함수가 새로운 state 로 덮어씌우는 과정을 은밀히 수행한다.

위의 removeProduct 함수는 매개변수로 상품의 아이디를 받아온다.
매개변수는 action.payload 에서 딕셔너리 형태로 받아올 수 있다.


📌 생성한 Redux 앱에 추가하기

이제 생성한 reducer 함수를 configureStore 함수에 추가한다.
( 여기서 Redux 의 장점이 나오는데 useContext 와 다르게 여러 곳에서 생성한 state 들을 한번에 모아서 정리할 수 있다.)

store/index.js

import { configureStore } from '@reduxjs/toolkit';
import cartSlice from './cart-slice';
import editProductSlice from './editProduct-slice';
import productSlice from './product-slice';
import uiSlice from './ui-slice';

const store = configureStore({
  reducer: {
    ui: uiSlice.reducer,
    cart: cartSlice.reducer,

    product: productSlice.reducer, // 방금 작성한 상품 목록 reducer 함수이다.

    editProduct: editProductSlice.reducer,
  },
});

export default store;


마지막으로 Provider 태그로 App 컴포넌트를 감싸고 위에서 생성한 store 변수를 props로 보내주면 이제 App에서 사용할 수 있게 된다.

index.js

import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import store from './store/index';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

📌 useSelector로 상품 목록 연동하기


이제 위의 상품 목록을 Redux 에서 관리하므로 위에서 생성한 product-slicestate 와 목록 상태를 연동해야 한다. 이 작업을 useSelector 함수로 구현한다.

products.js

import { useSelector } from 'react-redux';
import ProductItem from './ProductItem';
import classes from './Products.module.css';

const Products = (props) => {
  
  // useSelector 함수로 상품 목록 redux의 state를 받아온다.
  const productItems = useSelector((state) => state.product.items);

  return (
    <section className={classes.products}>
      <h2>Buy your favorite products</h2>
      <ul>
        {productItems.map((product) => (
          <ProductItem
            key={product.id}
            id={product.id}
            title={product.title}
            price={product.price}
            description={product.description}
          />
        ))}
      </ul>
    </section>
  );
};

export default Products;

useSelector 함수는 상태를 받아오는 역할만 수행한다.
( reducer 함수를 실행하는 역할은 useDispatch 가 수행한다. )

위의 useSelector 의 state.product.items는

// store/index.js
const store = configureStore({
  reducer: {
    product: productSlice.reducer
  },
});

configureStore에서 이름지은 product 의

// product-slice.js
const initialState = { items: [] };

createSlice 에서 처음의 상태를 정의한 items 를 가리킨 것이다.

📌 useDispatch로 상품 목록 추가 / 삭제하기

useSelector 함수가 상품의 state 를 받아오는 역할을 수행했다면 useDispatch 함수는 아까 만들어둔 reducer 함수를 호출해서 상품의 state 를 변경하는 역할을 수행한다.

위의 - 버튼을 누르면 상품이 목록에서 삭제되어야 한다.

EditProductItem.js

import { useDispatch } from 'react-redux';
import { productActions } from '../../store/product-slice';
import classes from './EditProductItem.module.css';

const EditProductItem = (props) => {

  const dispatch = useDispatch(); // useDispatch() 함수 사용

  const { id, title } = props.item;
  const removeItemHandler = () => {

    // productActions의 자동완성을 이용해서 상품 목록을 삭제하는 함수를 호출 & 실행한다.
    dispatch(productActions.removeProduct(id)); 
  };

  return (
    <li className={classes.item}>
      <header>
        <h3>{title}</h3>
        <button onClick={removeItemHandler}>-</button>
      </header>
    </li>
  );
};

export default EditProductItem;

상품을 삭제하려면 useDispatch 함수에서 매개변수로 상품의 ID 를 reducer 에 넘겨주어야 한다.

// product-slice.js

// 상품 목록에서 상품을 삭제하는 함수 (삭제할 상품의 ID를 매개변수로 받아옴)
    removeProduct(state, action) {
      const id = action.payload;
      state.items = state.items.filter((item) => item.id !== id);
    },

위에서 봤던 것처럼 reducer에서 매개변수를 action.payload 로 받아올 수 있다. ( 값이 여러개면 딕셔너리 형태로 넘긴다. )

이렇게 useDispatch 함수를 이용해서 Reduxstate 를 변경하는 것까지 구현해보았다. 상품 목록을 추가하는 기능도 구현했지만 Form 으로 상품의 정보를 입력받기 때문에 코드가 길어져서 생략하겠다. ( 로직은 동일하다. )

✏️ 다음 포스트

다음 포스트에선 업데이트 된 상품 목록 정보를 Firebase 에도 업데이트 하는 기능을 다뤄본다.

profile
"신은 주사위 놀이를 하지 않는다."

0개의 댓글