[React] Redux의 설정과 reducer 작성법

2019. 5. 20. 07:59Web/React.js

참고로 제 프로젝트의 폴더 구조는 아래와 같습니다. 개념 위주로 포스팅을 하다보니, 생략한 부분이 어느정도 있습니다. 궁금한 점은 댓글 남겨주시면 답변 드리겠습니다^^ (광고도 한번씩 클릭해주시면 감사하겠습니다 ㅠㅠ ㅋㅋㅋㅋㅋ)


redux는 정의된 Action과 Reducer에 의해 구독하고 있는 컴포넌트에 state를 전달하게 된다.

1) store/modules/ 폴더에 파일 생성 or 만드려는 reducer와 관련된 파일에 작성

2) action type, action creator, initial state, reducer 작성

import { createAction, handleActions } from 'redux-actions';

import { Map, fromJS } from 'immutable';
import { pender } from 'redux-pender';

import * as api from 'api/api';

// action types
const SEARCH_POI = 'SEARCH_POI';
const INITIALIZE_SEARCH_POI = 'INITIALIZE_SEARCH_POI';

// action creators
export const searchPoi = createAction(SEARCH_POI, api.searchPoi);
export const initialize = createAction(INITIALIZE_SEARCH_POI);

// initial state
const initialState = Map({
    pending: false,
    error: false,
    poiResults: Map()
});

// reducer
export default handleActions({
    ...pender({
        type: SEARCH_POI,
        onSuccess: (state, action) => {
            const { data: poiResults } = action.payload;
            return state.set('poiResults', fromJS(poiResults));
        },
    }),
    [INITIALIZE_SEARCH_POI]: (state, action) => {
        return state.set('poiResults', Map());
    }
}, initialState)
  • action type : 해당 액션이 어떤 작업을 하는 액션인지 정의, 대문자와 밑줄을 조합하여 만든다.

    {
        type: ACTION_TYPE,
        todo: {
            id: 1,
            text: 'action create',
            done: false
        }
    }
  • action creator : 액션을 직접 생성한다면 위와 같이 형식을 다 알고 있어야 하기 때문에, 액션을 만들어주는 역할을 하는 액션 생성 함수(action creator)를 이용

  • initial state : state의 초기값을 정의하는 것 (immutable 이용)

  • reducer : action 에 따라 state를 조작하여 셋팅하는 역할을 한다. (여기서는 redux-actions의 'handleActions'를 사용 -> switch-case문 대신 사용)

3) state의 변화가 필요한 곳에 action을 dispatch한다.

redux store에 직접 access하는 container component에서 connect 해준다.

...
import * as poiActions from 'store/modules/poi';
...

...
    handleChange = async (e) => {
        const { PoiActions } = this.props;
        const keyword = e.target.value;

        try {
          await PoiActions.searchPoi(keyword);
        } catch (error) {
          console.log(error);
        }
    }

    render() {
        ...

        return (
            ...
            <SearchForm ... handleChange={handleChange}></SearchForm>
        )
    }
...

export default connect(
  (state) => ({
    ...
  }),
  (dispatch) => ({
    PoiActions: bindActionCreators(poiActions, dispatch)
  })
)(PoiSearchContainer);
  • 여기서는 우선 (dispatch) 부분에 2)에서 정의한 액션 생성 함수를 Redux에 전달하겠다는 의미로 bindActionCreators를 사용하여 바인딩해준다.
  • 그 후에 이 컴포넌트에서 props로 사용하기 위해 여기서는 PoiActions에 맵핑시켜주었다.
  • 위의 return 부분의 JSX에서 Form에 변화가 있을 때 handleChange 함수가 수행되도록 하였고, handleChange에서 이전에 정의한 2)에서 만든 파일에서 action creator를 실행함으로써, state를 조작하게 된다.

4) containers 폴더에 컨테이너 컴포넌트 정의하여 사용

redux store에 직접 access하는 container component에서 connect 해준다.
여기서는 store를 구독하고 있는 컴포넌트라고 보면 된다. state의 변화가 생기면, 구독하고 있던 컴포넌트에 변화가 생겨 리렌더링이 발생하게 된다.

...
import { connect } from 'react-redux';
import { bindActionCreators } from "redux";

...
  render() {
    ...
    return (
        ...
        { !loading && !failure && poiResults && <PoiSearchResultContainer poiResults={poiResults} /> }
    )  
  }

export default connect(
  (state) => ({
    poiResults: state.poi.get('poiResults'),
    loading: state.pender.pending['SEARCH_POI'],
    failure: state.pender.failure['SEARCH_POI']
  }),
      ...
)(PoiSearchContainer);
  • 가장 아래에 모듈을 export하는 부분에 react-redux의 connect 함수를 걸어준다.
  • connect 함수는 redux store에 연결한다는 의미로 생각하면 된다.
  • (state)를 통해 store 저장된 state들을 가져와서 props에 맵핑하는 과정
  • 맵핑된 props는 위의 render 부분에서 이 컴포넌트의 props처럼 사용할 수 있다.