1. reducer로 state 로직 통합하기
2. useState
와 useReducer
비교하기
3. reducer 잘 작성하기
4. Immer를 사용하여 간결한 reducer 작성하기
useState
에서 useReducer
로 마이그레이션하는 방법은 세 단계로 진행된다.1-1. state 설정을 action들의 전달로 바꾸기
KeyNote
- action 객체는 어떤 형태든 될 수 있다.
- 그렇지만 무슨 일이 일어나는지 설명하는 문자열 타입의
type을
지정하고 추가적인 정보는 다른 필드를 통해 전달하도록 작성하는게 일반적이다.
1-2. reducer 함수 작성하기
function yourReducer(state, action) {
// return next state for React to set
}
tasks
)를 첫 번째 매개변수로 선언하라.action
객체를 두 번째 매개변수로 선언하라.function tasksReducer(tasks, action) {
if (action.type === 'added') {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false,
},
];
} else if (action.type === 'changed') {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
} else if (action.type === 'deleted') {
return tasks.filter((t) => t.id !== action.id);
} else {
throw Error('Unknown action: ' + action.type);
}
}
tasks
)를 매개변수로 갖기 때문에, 컴포넌트 밖에서 reducer 함수를 선언할 수 있다.KeyNote
- 위에 있던 코드는 if/else 구문을 사용한다.
그러나 reducer 안에서는 switch 구문을 사용하는 게 일반적이다.
결과는 똑같지만 switch 구문이 한눈에 봐도 읽기 더 편하다.- 하지만 아직 switch 구문에 익숙하지 않다면, if/else를 사용하는 것도 전혀 문제되지 않는다.
1-3. 컴포넌트에서 reducer 사용하기
tasksReducer
를 연결해야한다.useReducer
Hook을 import하라import { useReducer } from 'react';
useState
대신const [tasks, setTasks] = useState(initialTasks);
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
useReducer
Hook은 useState
와 비슷하다.
초기 state 값을 전달해야 하며, 그 결과로 state 값과 state 설정자 함수(useReducer의 경우 dispatch 함수)를 반환한다.
하지만 조금 다른 점이 있다.
useReducer
Hook은 두 개의 인자를 받는다.
그리고 아래 내용을 반환한다.
이렇게 관심사를 분리하면 컴포넌트 로직을 더 쉽게 읽을 수 있다.
이제 이벤트 핸들러는 action을 전달하여 무슨 일이 일어났는지 만 지정하고, reducer 함수는 action에 대한 응답으로 state가 어떻게 변경되는지 를 결정합니다.
useState
와 useReducer
비교하기코드 크기
- 일반적으로
useState
를 사용하면 미리 작성해야 하는 코드가 줄어든다.useReducer
를 사용하면 reducer 함수 와 action을 전달하는 부분 모두 작성해야 한다.
하지만 많은 이벤트 핸들러가 비슷한 방식으로 state를 업데이트하는 경우useReducer
를 사용하면 코드를 줄이는 데 도움이 될 수 있다.
가독성
useState
로 간단한 state를 업데이트 하는 경우 가독성이 좋다.- 그렇지만 state의 구조가 더욱 복잡해지면, 컴포넌트의 코드의 양이 부풀어 오르고 한눈에 읽기 어려워질 수 있다.
- 이 경우
useReducer
를 사용하면 업데이트 로직이 어떻게 동작 하는지와 이벤트 핸들러를 통해 무엇이 일어났는지 를 깔끔하게 분리할 수 있다.
디버깅
useState
에 버그가 있는 경우, state가 어디서 잘못 설정되었는지, 그리고 왜 그런지 알기 어려울 수 있다.useReducer
를 사용하면, reducer에 콘솔 로그를 추가하여 모든 state 업데이트와 왜 (어떤 action으로 인해) 버그가 발생했는지 확인할 수 있다.- 각 action이 정확하다면, 버그가 reducer 로직 자체에 있다는 것을 알 수 있다.
하지만useState
를 사용할 때보다 더 많은 코드를 살펴봐야한다.
테스팅
- reducer는 컴포넌트에 의존하지 않는 순수한 함수이다.
즉, 별도로 분리해서 내보내거나 테스트할 수 있다.- 일반적으로 보다 현실적인 환경에서 컴포넌트를 테스트하는 것이 가장 좋지만, 복잡한 state 업데이트 로직의 경우 reducer가 특정 초기 state와 action에 대해 특정 state를 반환한다고 단언하는 것이 유용할 수 있다.
개인 취향
- 어떤 사람은 reducer를 좋아하고 어떤 사람은 싫어한다.
취향의 차이이므로 괜찮다.
useState
와useReducer
는 언제든지 앞뒤로 변환할 수 있으며, 서로 동등하다.
useState
와 useReducer
를 함께 사용할 수도 있다.useState
를 useReducer
로 바꾼다.https://developer.mozilla.org/ko/