Action이 발생하면, Action을 실제로 처리하는 역할을 하는 함수
(Action은 State에 변화를 주기 위한 행동이다. 따라서, Reducer는 State에 변화를 주는 역할을 한다.)
Reducer는 현재 state를 기반으로 새로운 state를 만들어서 리턴한다.
// Reducer는 현재 State와 Action을 파라미터로 받아서 새로운 State를 만들어 리턴한다.
(state, action) => newState
예) 배열에 아이템을 넣고 빼기 위한 Action을 처리하는 Reducer 함수
function addReducer(state = initialState, action) {
switch (action.type) {
case 'PUSH_ITEM':
return [...state, action.item];
case 'POP_ITEM':
const newState = [...state];
newState.pop();
return newState;
default:
return state;
}
}
Reducer 함수는 파라미터로 state
와 action
을 받는다. 이후 action
객체에 필수로 포함되어 있는 type
값에 따라서, 상태에 정해진 변화를 준 뒤에 새로운 상태를 생성해서 리턴한다.
예를 들어, PUSH_ITEM
Action의 경우, 배열의 가장 마지막에 새로운 아이템을 추가하여 리턴하고,
POP_ITEM
Action의 경우, 배열의 가장 마지막에서 아이템을 하나 제거하고 리턴한다.
여기서 중요한 점은 파라미터로 받은 state가 아닌, 항상 새로운 state를 생성해서 리턴한다는 것이다.
새로운 state
는 파라미터로 받은 ‘현재state
’와 ‘action
‘ 객체를 기반으로 생성해야 한다.
외부의 다른 값을 참조하지 않고, 현재 state와 action객체에 들어있는 값만으로 새로운 state를 만들어야 한다.
현재 state를 조작할 수 없으며, 새로운 state를 만들어 `immutable(불변의) update`를 해야 한다.
Reducer가 순수 함수가 되려면 입력값(현재 state)을 조작하면 안되고, 새로운 state를 생성해야 한다.
비동기 로직
이나side effects
는 허용되지 않는다.
예를 들어, Reducer 내에서 서버와 통신을 해서 데이터를 받아오거나 하는 등의 동작을 하면 안 된다.
현재 State를 변경하지 않고, 새로운 State를 만들어 업데이트 하는 방식
현재 state에 있는 value값에 action 객체에 있는 새로운 value 값을 대입
function sampleReducer(state, action) {
switch (action.type) {
case "SAMPLE_ACTION":
state.value = action.value; // 기존 state를 직접 변경
return state;
default:
return state;
}
}
Spread 문법을 사용해 기존 state 객체의 데이터를 복사한 후, value키에 action 객체에 있는 새로운 value값을 넣어준다.
결과적으로 새로운 value 값이 들어간 객체가 새로 만들어진다.
function sampleReducer(state, action) {
switch (action.type) {
case "SAMPLE_ACTION":
return {
...state, // 기존 state를 복사 후, 새로운 state를 넣어준다.
value: action.value
};
default:
return state;
}
}
복잡한 state 객체를 업데이트 하는 경우
Redux Toolkit을 사용하면 복잡한 객체에 대해서도 손쉽게 불변적 업데이트를 할 수 있다. (내부적으로 immer라는 라이브러리를 사용하기 때문)
// 복잡한 state 객체를 업데이트할 때, Redux Toolkit을 사용하면 쉽게 불변적 업데이트를 할 수 있다.
function sampleReducer(state, action) {
switch (action.type) {
case "SAMPLE_ACTION":
return {
...state,
depth1: {
...state.depth1,
depth2: {
...state.depth1.depth2,
depth3: {
...state.depth1.depth2.depth3,
value: action.value
}
}
}
};
default:
return state;
}
}
리덕스를 사용하다보면 수많은 Action과 Reducer들이 생기게 된다.
이러한 Action과 Reducer는 공통된 카테고리로 모아서 분류할 수 있게 된다.
그런데 리덕스의 CreateStore()
함수에는 첫 번째 파라미터로 reducer
를 하나만 넣을 수 있기 때문에 모든 Action들을 처리하기 위해서는 나눠져 있는 reducer들을 하나로 합쳐야 한다.
이때 사용하는 함수가 바로 combineReducers()
이다.
combineReducer()
는 여러 개의 Reducer들을 하나로 합치는 역할을 한다.
이렇게 모든 Reducer들이 하나로 합쳐진 것을 rootReducer
라고 부르며, createStore()
함수의 첫 번째 파라미터로, 바로 이 rootReducer
가 들어가게 된다.
import { combineReducers } from 'redux';
import postReducer from './reducers/postReducer';
import commentReducer from './reducers/commentReducer';
// postReducer와 commentReducer를 합쳐서 하나의 rootReducer로 만든다.
const rootReducer = combineReducers({
post: postReducer,
comment: commentReducer,
});
export default rootReducer;
Reducer
(state, action) => newState
Reducer의 규칙들
불변적 업데이트(Immutable updates)
combineReducers()