[Web] React(19) /React Actions, Combine Reducers

최예린·2022년 9월 13일
0

React

목록 보기
12/19

reducer들을 어떻게 분리해서 상태의 부분들을 관리할지. 그리고 전체 상태를 관리하기위해서 reducer들을 어떻게 합치는지 공부해보겠습니다.

  1. 상태의 한 부분을 책임지는 리듀서들을 실행하는 방법
  2. 전체 상태를 관리하기위해서 리듀서들을 합치는 방법

Redux Actions이란?

Action

action은 하나의 타입 필드를 갖는 플레인 자바스크립트 객체이다. 액션은 애플리케이션에서 발생하는 무언가를 설명하는 사건이라고 생각할 수 있다.

Reducer

reducer 는 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수이다.

function reducer(state, action) {
  // 새로운 상태를 만드는 로직
  // const nextState = ...
  return nextState;
}

(state, action) => newState
간단하게 리듀서는 받아온 액션(이벤트)의 타입에 근거하여 이벤트를 핸들링하는 event listener라고 생각할 수 있다.

Reducers 쪼개기

애플리케이션이 복잡할때, 특히 애플리케이션이 많은 부분 상태들로 구성될 때 리듀서를 여러개의 함수로 쪼개는 것이 더 쉽습니다. 상태의 형태에 따라 리듀서를 다수의 함수로 쪼갤 수 있으며 각각의 함수들은 상태의 한 부분을 관리할 것입니다.

redux 디렉토리에 dishes.js, comments.jsm promotions.js, leaders.js 파일을 생성해서 리듀서들을 분리해줍니다.

redux/dishes.js 생성하기

import { DISHES } from '../shared/dishes';

export const Dishes = (state = DISHES, action) => {
    switch (action.type) {
        default:
          return state;
      }
};

상태가 정의되어있지않거나 default상태이면 DISHES로 초기화합니다.
여기서 초기화를 해주기때문에 configureStore에서는 initialState가 더이상 필요없어지기때문에 이따가 아래에서 수정해주겠습니다.

redux/comments.js 생성하기

import { COMMENTS } from '../shared/comments';

export const Comments = (state = COMMENTS, action) => {
    switch (action.type) {

        default:
          return state;
      }
};

나머지 세개도 dishes와 마찬가지로 새롭게 파일을 생성해서 리듀서들을 분리해줍니다.

redux/promotions.js 생성하기

import { PROMOTIONS } from '../shared/promotions';

export const Promotions = (state = PROMOTIONS, action) => {
    switch (action.type) {
        default:
          return state;
      }
};

redux/leaders.js 생성하기

import { LEADERS } from '../shared/leaders';

export const Leaders = (state = LEADERS, action) => {
    switch (action.type) {
        default:
          return state;
      }
};

이제 reducer.js 파일을 삭제해도 됩니다.

Reducers 합치기

상태 관리를 부분 상태를 관리하는 서로 다른 리듀서들로 분리했습니다.
이제 그것들을 합쳐볼겁니다.
configureStore.js 파일을 열어서 코드를 다음과 같이 업데이트 합니다.

configureStore.js

import {createStore, combineReducers} from 'redux';
import { Dishes } from './dishes';
import { Comments } from './comments';
import { Promotions } from './promotions';
import { Leaders } from './leaders';

export const ConfigureStore = () => {
    const store = createStore(
        combineReducers({
            dishes: Dishes,
            comments: Comments,
            promotions: Promotions,
            leaders: Leaders
        })
    );

    return store;
}

리듀서들을 합칠 때는 redux에서 제공하는 combineReducers를 사용합니다.

Actions 생성하기

redux/ActionTypes.js 생성하기

export const ADD_COMMENT = 'ADD_COMMENT';

ActionCreators.js 생성하기

import * as ActionTypes from './ActionTypes';

export const addComment = (dishId, rating, author, comment) => ({
    type: ActionTypes.ADD_COMMENT,
    payload: {
        dishId: dishId,
        rating: rating,
        author: author,
        comment: comment
    }
});

comments.js

import { COMMENTS } from '../shared/comments';
import * as ActionTypes from './ActionTypes';

export const Comments = (state = COMMENTS, action) => {
    switch (action.type) {
        case ActionTypes.ADD_COMMENT:
            var comment = action.payload;
            comment.id = state.length;
            comment.date = new Date().toISOString();
            console.log("Comment: ", comment);
            return state.concat(comment);

        default:
          return state;
      }
};

액션이 ActionCreator.js에 의해 디스패치됐을때
액션을 초기화하도록 코드를 업데이트해줍니다.

  • A.concat(B)
    두개의 문자열(배열)을 하나의 문자열(배열)로 만들어주는 함수입니다. 기존의 것들을 변경하지않으며 새로 배열을 생성해서 반환합니다.

MainComponent.js

. . .

import { addComment } from '../redux/ActionCreators';

. . .

  const mapDispatchToProps = dispatch => ({
  
    addComment: (dishId, rating, author, comment) => dispatch(addComment(dishId, rating, author, comment))
  
  });

. . .

      <DishDetail dish={this.props.dishes.filter((dish) => dish.id === parseInt(match.params.dishId,10))[0]}
        comments={this.props.comments.filter((comment) => comment.dishId === parseInt(match.params.dishId,10))}
        addComment={this.props.addComment}
      />

. . .

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));

DishdetailComponent.js 내에서 사용할 수 있도록 MainComponent.js 코드를 업데이트 해줍니다.

DishdetailComponent.js

. . .

  function RenderComments({comments, addComment, dishId}) {



. . .

      <CommentForm dishId={dishId} addComment={addComment} />


. . .

        this.props.addComment(this.props.dishId, values.rating, values.yourname, values.comment);



. . .

      <RenderComments comments={props.comments}
        addComment={props.addComment}
        dishId={props.dish.id}
      />

. . .

마지막으로 사용자가 제출한 댓글폼에 따라서 액션을 초기화하도록 DishdetailComponent.js를 업데이트해줍니다.

이제 comment를 작성하고 submit하면 특정 dishId의 comment list에 새롭게 추가되는걸 확인할 수 있습니다.

액션을 사용하여 리덕스 스토어를 변경하고 즉시 리액트 애플리케이션 뷰에 반영하는 방법을 알아보았습니다.

profile
경북대학교 글로벌소프트웨어융합전공/미디어아트연계전공

0개의 댓글