티스토리 뷰

Web/React

React-redux 정리

Hesh 2021. 11. 14. 18:48

오늘 내내 공부했는데도 조금 복잡하긴 하다.

context API를 통하여 상위 컴포넌트에서 원하는 컴포넌트까지 줄줄이 전달하지 않아도 바로 전달할 수 있게 되었지만 이전 버전에서는 사용이 불편했다.

context API가 예전보단 사용하기 편해졌지만 그 대책이었던 Redux가 큰 프로젝트에서 효율적이고 상태를 한눈에 보기 편해서 여전히 잘 쓰인다.

 

Store

스토어에는 애플리케이션의 상태 (state)와 리듀서들이 들어가있다.

스토어는 프로젝트에 단 하나만 존재할 수 있으며 내장함수로 dispatch와 subscribe를 가진다.

  • dispatch : 파라미터로 넘어온 액션 객체 (함수)를 리듀서에 전달하여 상태를 업데이트한다.
  • subscribe : 리스너 함수를 넣어서 호출해주면 리스너 함수가 상태가 업데이트될 때마다 호출된다

Reducer

리듀서는 store에 저장되어 액션에 따라 변화를 일으키는 함수이다.

Ducks 패턴에서는 파일 하나에 액션 타입, 생성함수와 리듀서와 state를 한꺼번에 같이 써준다.

import { createAction, handleActions } from 'redux-actions';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';

export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });

const initialState = {
  number: 0,
};

function counter(state = initialState, action) {
  switch (action.type) {
    case INCREASE:
      return {
        number: state.number + 1,
      };
    case DECREASE:
      return {
        number: state.number - 1,
      };
    default:
      return state;
  }
}

export default counter;

액션을 만들어 발생시키면 리듀서가 현재 상태와 액션을 받아와서 액션에 따라 상태를 업데이트시킨다.

createStore에서는 리듀서를 하나만 써야 하기 때문에 combineReducers 를 이용하여 하나로 묶는다.

import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';

/* 
   스토어를 만들 때는 리듀서를 하나만 써야해서 묶어준다.
   파일 이름이 index.js이면 import rootReducer from './디렉토리명'만으로도 불러올 수 있다
*/
const rootReducer = combineReducers({
  counter,
  todos,
});

export default rootReducer;

 

Action

Action을 만들 때는 이름을 고유하게 만들어줘야 하는데 '모듈 이름/액션 이름'으로 해주면 충돌이 안 일어나게 된다.

액션 생성함수를 만들어주어 액션 객체를 만들고 dispatch에 실어주게 되면 Store에서 알맞은 Reducer에게 전달해준다.

react에서는 connect 함수로 묶어줘야 store와 연동될 수 있다.

export default connect(
  (state) => ({
    number: state.counter.number,
  }),
  (dispatch) => ({
    increase: () => dispatch(increase()),
    decrease: () => dispatch(decrease()),
  })
)(CounterContainer);

connect 를 통하여서 store와 연동시켜주면 state와 dispatch를 store에서 받아오고

number와 increase, decrease와 같은 state와 action함수들을 CounterContainer의 props로 넘겨줄 수 있게 된다.

 

 

더보기

+물론 Hooks를 이용하게 되면 이런 날먹도 가능하다.

 

useSelector를 통하여서 바로 store.state에 접근할 수 있고

useDispatchstore.dispatch를 곧장 이용할 수 있다.

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease } from '../modules/counter';

const CounterContainer = () => {
  const number = useSelector((state) => state.counter.number);
  const dispatch = useDispatch();
  return (
    <Counter
      number={number}
      onIncrease={() => dispatch(increase())}
      onDecrease={() => dispatch(decrease())}
    />
  );
};

// connect와 달리 상위 컴포넌트와 상관없이 자신의 props만 안바뀌면 리렌더링을 방지해주는 기능이 사라진다.
// 따라서 useSelector를 이용하게되면 connect와 달리 React.memo를 써줘야 최적화가 된다.
export default React.memo(CounterContainer);

또한 모듈에서도 createAction으로 액션 생성함수를 만들어 줄 수 있는데 2번째 파라미터로 필요한 state를 넘겨줄 수 있다. (단 넘어가서 받을때는 이름 구분없이 payload로 받는다.)

또한 handleActions로 리듀서를 편하게 쓸 수 있다.

import { createAction, handleActions } from 'redux-actions';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';

export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);

const initialState = {
  number: 0,
};

const counter = handleActions(
  {
    [INCREASE]: (state, action) => ({ number: state.number + 1 }),
    [DECREASE]: (state, action) => ({ number: state.number - 1 }),
  },
  initialState
);

export default counter;

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday