이번 포스팅은 useReducer에 대해 알아보려고 한다. 먼저 reducer의 역할은 컴포넌트 내부에 state변수를 업데이트하는 이벤트핸들러가 여러 개를 작성하여 사용하는 경우가 있다. 점점 이벤트 핸들러가 많아지게 된다면 컴포넌트의 내부의 코드량은 점점 증가한다. 이 문제를 해결하기 위해 state변수를 업데이트하는 로직을 reducer로 활용하여 컴포넌트 외부에서 통합하여 관리하는 역활을 한다.
그렇기 때문에 무조건적으로 reducer를 사용하는 것이 아닌 useState의 set함수를 활용한 이벤트핸들러를 구현하여 사용하는것과 같은 역할을 하기 때문에 사람들마다 reducer를 사용하는 사람과 사용하지 않는 사람으로 나눌 수 있다.
◼ useState와 useReducer 차이점
먼저 useState와 useReducer의 차이점에 대해서 살펴보고 가도록 하자.
1. 코드작성량
useReducer를 사용하기 위해서는 기분적으로 reducer 함수와 action을 전달하는 부분을 작성해줘야 한다. 그렇기 때문에 간단한 컴포넌트의 경우 useState를 활용하는 것이 유리하다. 하지만 state가 업데이트되는 핸들러의 양이 많아질수록 useReducer를 사용하면 코드량을 줄일 수 있다.
2. 가독성
useState를 활용하여 간단한 state변수를 업데이트하는 것은 가독성이 좋으나, 복잡한 구조의 state변수를 다루게 된다면 useReducer를 활용하여 동작과 핸들러의 역할을 명확하게 구분할 수 있다.
위의 2가지 이외의 다양한 차이점이 있으나 가장 중요한 것은 가독성과 코드 작성량이라고 생각되어 2가지에 대해 비교해 보았다.
◼ useReducer의 기본 구조
useReducer의 파라미터와 반환값에 대해서 살펴보도록 하자.
먼저 파라미터에 대해 알아보자. (*은 필수 파라미터)
파라미터명 | 역활 및 설명 |
* reducer | state와 action에 파라미터로 받는 순수 함수 |
* initialArg | state의 초기값(init함수 파라미터) |
init | 초기의 State를 반환하는 init 함수 |
위의 initalArg와 init의 역할이 조금 난해하게 설명되어 있지만 한 가지의 예로 완벽하게 이해가 될 것이다.
init의 경우 필수 파라미터가 아닌 것을 볼 수 있다. 만약 init값이 있을 경우의 초기 State변수는 init(initialArg)의 결괏값이며, init 값이 없을 경우 initialArg의 값이 State변수가 된다.
아래 코드는 파라미터로 받는 reducer의 기본 구성을 예로 작성한 것이다.
function reducer(state, action) {
if (action.type === 'incremented_age') {
return {
age: state.age + 1
};
}
throw Error('Unknown action.');
}
다음은 반환값에 대해서 살펴보도록 하겠다. 먼저 state는 우리가 알고 있는 그 State변수를 말한다. 다음으로 dispatch는 state변수를 새로운 값으로 업데이트하고 리렌더링을 발생시키는 역학을 한다. dispatch의 인수는 action이다. 아래는 dispatch의 사용에 대한 대한 코드이다.
const [state, dispatch] = useReducer(reducer, { age: 42 });
function handleClick() {
dispatch({ type: 'incremented_age' });
// ...
useReducer의 파라미터 중 reducer에 명시해 놓은 action의 조건대로 dispatch가 실행된다.
◼ useState 대신 useReducer 사용하기
기존에 사용했던 useState와 이벤드핸들러를 활용한 state변수 업데이트가 아닌 useReducer를 활용하여 state변수를 업데이트하는 코드로 바꿔보겠다.
먼저 아래의 코드는 useState를 활용하여 작성한 코드이다. 해당 코드를 useState가 하는 역할을 useReduce를 사용하여 변경해 보도록 하겠다.
먼저 useState를 useReducer로 변경하기 위해서는 3가지 단계가 필요하다.
1. State변수를 set을 활용하여 설정하는 것에서 dispatch로 변경하기
2. reducer 함수 작성하기
3. 컴포넌트에서 reducer를 사용하기
위와 같이 단계로 구성하여 하나씩 진행해 보도록 하겠다.
1. state변수를 dispatch로 변경하기
먼저 set을 활용하여 작성돼있는 코드를 아래와 같이 dispatch를 활용한 State변수를 업데이트하는 코드로 변경한다.
//기존의 set을 활용한 State변수 수정
function handleAddTask(text) {
setTasks([...tasks, {
id: nextId++,
text: text,
done: false
}]);
}
//dispatch를 활용한 State변수 설정
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
2. Reducer 함수를 작성하기
dispatch를 활용하여 state변수를 업데이트하기 위해서는 reducer가 필요하다. 위에서 언급했듯이 useReducer의 필수 파라미터 중에는 reducer가 있다. 그렇기 때문에 useState에서의 set의 역할과 같은 역할을 할 수 있도록 reducer를 작성해 준다. 첫 번째 단계에서 작성한 핸들러별 기능을 reducer 내부에서 action별로 실행되도록 아래의 코드와 같이 작성한다.
function tasksReducer(tasks, action) {
if (action.type === 'added') {
//handleAddTask 역활
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);
}
}
3. 컴포넌트에서 reducer사용하기
앞선 단계에서 수정하고 작성한 내용을 바탕으로 useReducer를 활용하여 useState역할을 대신해 보도록 하겠다.
아래와 같이 기존의 useState를 활용하여 state변수를 업데이트했던 코드를 useReducer를 활용하여 업데이트할 수 있는 코도록 변경했다.
'개발 > React' 카테고리의 다른 글
👀 useRef란? #9 (1) | 2024.01.10 |
---|---|
👀 useContext란? #8 (0) | 2024.01.06 |
👀 State 변수 초기화 및 보존 #6 (0) | 2024.01.04 |
👀 컴포넌트 렌더링 과정 #5 (2) | 2024.01.03 |
👀 useState란? #4 (0) | 2023.12.23 |