reducer들을 어떻게 분리해서 상태의 부분들을 관리할지. 그리고 전체 상태를 관리하기위해서 reducer들을 어떻게 합치는지 공부해보겠습니다.
action은 하나의 타입 필드를 갖는 플레인 자바스크립트 객체이다. 액션은 애플리케이션에서 발생하는 무언가를 설명하는 사건이라고 생각할 수 있다.
reducer 는 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수이다.
function reducer(state, action) {
// 새로운 상태를 만드는 로직
// const nextState = ...
return nextState;
}
(state, action) => newState
간단하게 리듀서는 받아온 액션(이벤트)의 타입에 근거하여 이벤트를 핸들링하는 event listener라고 생각할 수 있다.
애플리케이션이 복잡할때, 특히 애플리케이션이 많은 부분 상태들로 구성될 때 리듀서를 여러개의 함수로 쪼개는 것이 더 쉽습니다. 상태의 형태에 따라 리듀서를 다수의 함수로 쪼갤 수 있으며 각각의 함수들은 상태의 한 부분을 관리할 것입니다.
redux 디렉토리에 dishes.js, comments.jsm promotions.js, leaders.js 파일을 생성해서 리듀서들을 분리해줍니다.
import { DISHES } from '../shared/dishes';
export const Dishes = (state = DISHES, action) => {
switch (action.type) {
default:
return state;
}
};
상태가 정의되어있지않거나 default상태이면 DISHES로 초기화합니다.
여기서 초기화를 해주기때문에 configureStore에서는 initialState가 더이상 필요없어지기때문에 이따가 아래에서 수정해주겠습니다.
import { COMMENTS } from '../shared/comments';
export const Comments = (state = COMMENTS, action) => {
switch (action.type) {
default:
return state;
}
};
나머지 세개도 dishes와 마찬가지로 새롭게 파일을 생성해서 리듀서들을 분리해줍니다.
import { PROMOTIONS } from '../shared/promotions';
export const Promotions = (state = PROMOTIONS, action) => {
switch (action.type) {
default:
return state;
}
};
import { LEADERS } from '../shared/leaders';
export const Leaders = (state = LEADERS, action) => {
switch (action.type) {
default:
return state;
}
};
이제 reducer.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를 사용합니다.
export const ADD_COMMENT = 'ADD_COMMENT';
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
}
});
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)
두개의 문자열(배열)을 하나의 문자열(배열)로 만들어주는 함수입니다. 기존의 것들을 변경하지않으며 새로 배열을 생성해서 반환합니다.
. . .
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 코드를 업데이트 해줍니다.
. . .
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에 새롭게 추가되는걸 확인할 수 있습니다.
액션을 사용하여 리덕스 스토어를 변경하고 즉시 리액트 애플리케이션 뷰에 반영하는 방법을 알아보았습니다.