전화번호부 프로젝트에 Redux를 적용해봅시다.
먼저, Redux는 하나의 스토어만 만들 수 있지만 리듀서 함수의 개수는 정해져 있지 않습니다. 특정 함수를 통해 리듀서 함수를 하나로 통합하여 내보낼 수 있기 때문입니다. 따라서 이번 페이지에서는 전화번호부 데이터를 담는 리듀서 함수, 인풋값을 관리하는 리듀서 함수로 총 2개의 리듀서 함수를 만들고, 하나로 합쳐 스토어를 만들어보겠습니다. 먼저 데이터를 관리하는 리듀서 함수를 작성해봅시다. 아래와 같이 작성해주세요.
import { createAction, handleActions } from 'redux-actions';
import produce from 'immer';
const APPEND_DATA = 'data/APPEND_DATA';
const REMOVE_DATA = 'data/REMOVE_DATA';
export const appendData = createAction(APPEND_DATA, data => data);
export const removeData = createAction(REMOVE_DATA, id => id);
const initialState = {
"0": {
id: "0",
name: "John",
phone: "010-0000-0000"
},
"1": {
id: "1",
name: "Peter",
phone: "010-1111-1111"
},
"2": {
id: "2",
name: "Elise",
phone: "010-2222-2222"
},
"3": {
id: "3",
name: "Chris",
phone: "010-3333-3333"
},
"4": {
id: "4",
name: "Austin",
phone: "010-4444-4444"
},
"5": {
id: "5",
name: "Adam",
phone: "010-5555-5555"
}
};
var nextId = Object.keys(initialState).length;
export default handleActions({
[APPEND_DATA]: (state, action) =>
produce(state, draft => {
draft[nextId] = {
id: nextId,
...action.payload
};
nextId++;
}),
[REMOVE_DATA]: (state, action) =>
produce(state, draft => {
delete draft[action.payload];
})
}, initialState);
먼저 이 리듀서 함수는 2개의 액션이 필요합니다. 데이터를 추가하는 것과 삭제하는 것입니다. 따라서 각각의 액션 타입을 APPEND_DATA, REMOVE_DATA로 하겠습니다. 그리고 아래 액션 생성자를 만들어 주었습니다. appendData 함수는 이름과 전화번호가 담긴 data 객체를 인자값으로 받고, removeData 함수는 id 값을 인자값으로 받습니다.
다음 코드는 initialState와 nextId 부분인데 해당 부분은 지난 챕터의 dummyData.js 파일에 존재하는 코드와 동일하므로 건너뛰겠습니다.
마지막으로 리듀서 함수 부분입니다. 먼저 APPEND_DATA 타입에 대한 코드로는 draft에 새로운 id 값과 액션 함수를 통해 받은 데이터를 함께 추가해줬습니다. 그리고 nextId의 값을 1만큼 더해주었습니다. 그리고 REMOVE_DATA에 대한 코드로는 draft에서 특정 key 값을 통해 delete 함수로 해당 값을 지워주었습니다. 저번에 state를 사용했던 방식은 Destructing Assignment 방식으로, 해당 id 값에 따른 데이터와 나머지 데이터로 분류하여 그 나머지 데이터를 새로운 상태로 대체했었습니다. 불변성을 지키기 위해서이죠. 하지만 immer를 사용하면 delete 함수를 사용해도 알아서 불변성을 지켜주니 상관없습니다.
다음으로는 인풋값을 관리해주는 리듀서 함수를 작성해보겠습니다. 다음과 같이 작성해주세요.
import { createAction, handleActions } from 'redux-actions';
import produce from 'immer';
const SET_INPUT_VALUE = 'input/SET_INPUT_VALUE';
export const setInputValue = createAction(SET_INPUT_VALUE, data => data);
const initialState = {
name: "",
phone: ""
};
export default handleActions({
[SET_INPUT_VALUE]: (state, action) =>
produce(state, draft => {
const { name, value } = action.payload;
draft[name] = value;
})
}, initialState);
이 리듀서 함수는 인풋값을 바꾸는 하나의 액션만 있으면 됩니다. 편의에 따라 인풋값을 모두 지우는 액션도 추가할 수 있겠지만, 해당 챕터에서는 인풋값을 변경하는 액션 하나만 가지고 진행하겠습니다. 액션 타입과 함수를 정의했습니다. 그리고 액션 함수로 name, value가 들어있는 객체를 받도록 명시해두었습니다. initialState 부분은 건너뛰겠습니다. 마지막으로 리듀서 함수 부분에서는 action의 payload 속성으로부터 name과 value 값을 가져와 draft에서 직접 수정해주었습니다. 불변성은 immer 라이브러리에서 알아서 보장해주기 때문에 신경 쓰지 않아도 괜찮습니다.