개인프로젝트(React+Redux)

yonghee·2022년 1월 4일
0

개인프로젝트

목록 보기
2/8

Redux 초기 설정하기

1.<Provider 적용>

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

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

를 import하고, state값의 공유를 원하는 컴포넌트를 다 감싼다.그리고 Redux에서 사용하는 오직 하나의 저장소 store를 할당한다
위에서보면, App 컴포넌트와 그 안에 있는 모든 HTML, 컴포넌트들은 전부 state를 직접 props 전송없이 사용할 수 있게된다.

2. createStore() 적용

import { createStore } from 'redux';
import rootReducer from '../reducers/index'

const store = createStore(rootReducer)

export default store;

말 그대로 store state가 관리되는 오직 하나뿐인 저장소의 역할을 합니다. Redux 앱의 state가 저장되어 있는 공간이다. createStore 메서드를 활용해 reducer를 연결시킨다 createStore와 더불어 다른 리듀서의 조합을 인자로 넣어서 스토어를 생성할 수 다.

3.reducer 만들기

import  { combineReducers } from 'redux';
import ItemReducer from './ItemReducer';

const rootReducer = combineReducers({
        ItemReducer
});

export default rootReducer;

  
import { ADD_CART, REMOVE_CART, SET_QUANTITY } from "../actions/index"
import { initialState } from '../asset/data'
console.log(initialState);

const ItemReducer = (state = initialState, action:any) => {
   
      return state;
        }}

export default ItemReducer

index 파일 안에 reducer들을 관리 할수가 있는데 combineReducers을 통해 한번에 관리가 가능하다 현재 ItemReducer components만 사용중이다.

Action을 통해 받아 state를 ItemReducer에서 새로운 State를 반환한다.

4.Action 만들기

//액션 타입 선언
export const ADD_CART = "ADD_CART";
export const REMOVE_CART = "REMOVE_CART";
export const SET_QUANTITY ="SET_QUANTITY"

//액션 생성 선언 함수
export const AddCart = (itemId:number) => {
  console.log(itemId);
  return {
     
      }}}

export const RemoveCart = (itemId:number) => {
  console.log(itemId);
  return {
      
      }}}

export const SetQuantity = (quantity:number, itemId:number) => {
  console.log(quantity, itemId);
  return {
      
      }}}

버튼을 클릭 하였을 때 action을 취하게 된다 액션 타입을 선언하고 액션 생성 함수를 구성한다. type은 필수로 지정을 해 주어야 한다. 초기 큰틀에서 설정하는 것이 어려웠다. 버튼을 클릭 하였을 때 부터 시작하는게 맞는건지 사실 그렇게 해도 설정은 됐을 것 같지만 index 컴포넌트에서 provider로 감싸는 것 부터 시작하여 store={store}를 할당하며 에러 발생한 것부터 역순 방향으로 해결해 나가며 구성하니 해결이 되었다.

Redux로 개발하기

초기 구성은 다 하였다 그럼 이제

그림 과 같은 패턴으로 흐름 기억하며 만들어 보도록 한다.

main 페이지 구성하기

메인 페이지에는 아이템 리스트들은 보여주며 클릭 하였을 때 장바구니로 추가가 되고 장바구니에 추가되는 기능을 갖고 있다.

import React from 'react';
import { AddCart } from '../actions';
import { useSelector, useDispatch } from 'react-redux';
import './Main.css';

export interface ItemReducer {
        ItemReducer : Array<object>
}

export interface DataSetting {  
  items: [{
    price:number;
    id:number;
    img:string;
    name:string;
    text:string;
  }]
  cartItems: [{
    quantity:number;
    itemId:number;
  }]; 
}

const Main = () => {
  const state:any = useSelector<ItemReducer>(state=> state.ItemReducer);
  const dispatch = useDispatch();
  const {items, cartItems}:DataSetting = state;
  console.log({items, cartItems})
  
  const AddCartSetting = ( itemId:number ) => {    
    const find = cartItems.filter((ele):boolean => (ele.itemId === itemId))[0]
    if(!find) {
      console.log(find);
      console.log('새로운 상품 추가')
      dispatch(AddCart(itemId))
    }
    else {
      console.log('기존 리스트와 일치하는 상품')
    }
  }

    return (  
            <div className="MainBox">          
            {items.map((item) => (
            <div className="Box" key={item.id}>  
              <img className="ImgBox" src={item.img} alt="" />
              <div className="ItemInfo">{item.name} {item.price}</div>
              <span className='Text'>{item.text}</span>
              <div className='BtnBox'>
              <button className="ItemBtn" onClick={() => AddCartSetting(item.id)}>장바구니 추가</button>
              </div>
            </div>              
            ))}                       
        </div>
    )
}
export default Main;

일단 처음에는 내장 되어있는 데이터를 갖고 와야 되는 것인데 그것은 오직 하나 뿐인 저장소 store에 보관 되어 있다. 이것을 가져 올 때 props를 해줄 필요 없이 useSelector를 통해 어디서든 쉽게 가져 올수가 있다.

const state:any = useSelector<ItemReducer>(state=> state.ItemReducer);  

가져 온 값을 map 메서드로 출력해주면 된다. 이 부분은 다른 점이 없다 그리고 이제 버튼 을 클릭 하였을 때 아이템이 추가 되는 메서드를 실행 시켜야 하는데 더 이상 useState를 사용하지 않기 때문에 상태 변경을 하려면 Action에서 실행하고자 지정한 타입을
const dispatch = useDispatch(); useDispatch()를 통해 reducer로 보내주게 된다
그러면 이제 Action 타입을 구성할 차례이다

Action

export const AddCart = (itemId:number) => {
    console.log(itemId);
    return {
        type: ADD_CART,
        payload : {
            quantity: 1,
            itemId
        }
    }
}  

액션 생성 함수를 보게 되면 아이템의 아이디를 받아오게 되며 타입을 지정해 주면 된다.
그럼 이제 reducer를 보도록 해보면

reducer

useDispatch를 통해 가져온 값은 type의 값과 payload의 값을 확인 할 수 있다.
payload 값을 cartItems에 ...state.cartItems를 통해 복사해 온 후에 객체 값으로 넣어주고 Object.assign()를 통해 기존 객체 값들과 합쳐 주면 된다.
Object.assign()에 대한 설명은 Redux 블로깅에 따로 정리를 해두엇다.

  const ItemReducer = (state = initialState, action:any) => {
    console.log(state);
    console.log(action);    
    switch (action.type) {
        case ADD_CART:
          return Object.assign({}, state, { cartItems: [...state.cartItems, action.payload]})
        case REMOVE_CART:
          return Object.assign({}, state, {
            cartItems: state.cartItems.filter((ele:any) => ele.itemId !== action.payload.itemId )
          })
        case SET_QUANTITY:
            let idx = state.cartItems.findIndex((ele:any) => ele.itemId === action.payload.itemId)
          return Object.assign({}, state, {
            cartItems: [...state.cartItems.slice(0, idx), action.payload,
            ...state.cartItems.slice(idx + 1)]
          });
                  
            default:
      return state;
        }
    
}
profile
필요할 때 남기는 날것의 기록 공간

0개의 댓글