React를 배울 때 처음 배운 후크를 선택한다면 95%의 확률로 useState가 될 것이다.
그만큼 실무에서도 자주 사용되는 Hook 이며 React 의 근본이라고 할 수 있는 Hook 입니다만, 만약 이 useState 를 관리 또는 갱신하는 함수가 복수개가 되면 어떻게 될까요. 관리도 어려워지고 가독성도 저하된다.
이 때 상태를 관리하는 Hook 중 useState 이외의 방법이 useReducer이다.
이 Hook을 사용하면 상태 업데이트에서 해당 업데이트 함수를 구성 요소에서 분리할 수 있습니다.
그렇게 하면, 이러한 기능만을 모아 관리하는 파일이나 Custom Hook를 작성할 수 있고, 다른 파일에 작성 후 호출해 사용할 수도 있다.
Reduce
첫째, 정말 간단하게, 이 Reduce가 무엇을 의미하는지 살펴 보겠습니다.
Reduce는 감소합니다.
라는 의미를 가지고 있다.
그렇다면 무엇을 줄일까요?
별도 공식 문서에 쓰여지지 않았지만 개인적으로 useState를 관리하는 코드를 줄일 수 있다고 생각한다.
컴퍼넌트내에서 매회 파일마다 값을 변경하는 함수를 매회 선언해 사용하는 것이 아니라, 공유로 쓸 수 있도록(듯이) 컴퍼넌트와 분리해 파일 마다 선언해야 하는 수고를 줄이는 것이다.
useReducer의 기본 구성
useReducer의 기본 구성은 다음과 같습니다.
import React, { useReducer } from "react";
const (state, dispatch) = useReducer(reducer, initialState, init);
- state : 읽어들이는 컴퍼넌트에서 사용 및 관리하는 상태
- dispatch: useReducer 함수에서 첫 번째 인수인 reducer 함수를 실행하는 키워드
- reducer : 구성 요소 외부 (또는 내부)에서 상태를 업데이트하는 함수입니다.
선언한 state 또는 initialState 와 action 객체를 인수로서 사용해, 기존의 state 를 새롭게 갱신 가능합니다. - initialState: 초기화할 State 값
- init: 초기 함수. initialState를 조금 지연시켜 생성하는 데 사용됩니다.
값을 초기화할 때 쓰기도 합니다.
총 5개의 구성이 있으며, init의 경우는 option이라고 선언하지 않아도 사용할 수 있다.
이제 reducer 함수를 실행하는 디스패치에 대해 조금 더 자세히 살펴 보겠습니다.
dispatch 과 reducer
연예인의 프라이버시 캐릭터는 연예계 뉴스와 비슷합니다.
이 함수는 위에서 설명한 useReducer 인수 중 하나인 reducer에서 선언된 함수를 호출하는 데 사용됩니다.
그럼, dispatch를 알기 위해서 이 reducer의 구조와 dispatch의 사용법을 조사하면
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + action.payload };
case "DECREMENT":
return { count: state.count - action.payload };
}
}
const Counter = () => {
const initialState = { count: 0 };
const (state, dispatch) = useReducer(reducer, initialState);
return (
<>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>
+
</button>
<button onClick={() => dispatch({ type: "DECREMENT", payload: 1 })}>
-
</button>
</>
);
};
export default Counter;
위의 코드를 보면, 우선 reducer 와는 함수 안에 인수로서 state 와 action 을 받은 후, switch 문을 사용해 action 내의 type 가 어떤 값인지에 따라 return 하는 값이 다르게 된다.
이와 같이 선언한 후, 컴퍼넌트에서는 initialState 로 기본 state 를 결정하고 나서 useReducer Hook 를 사용해 useState 와 같이 state 와 그 state 를 변경하는 함수를 넣어 주면 준비가 완료했다.
그런 다음 dispatch를 사용하여 첫 번째 인수로 useReducer에서 첫 번째 인수의 값에 넣은 reducer 함수의 값을 넣고 두 번째 인수는 state에 얼마나 많은 변화를주는지 payload 값에 넣어 주면 실행이 완료된다.
useReducer는 언제 작성해야합니까?
useReducer의 설명을 보면, 이런 생각이 듭니다.
useState와 INCREMENT, DECREMENT 함수를 각각 따로 선언해도 괜찮은 것은?
그렇네요 그렇게 해도 좋다.
그냥 한 번 생각해 보자.지금 실행하거나 선언하는 함수 INCREMENT, DECREMENT가 2개뿐입니다만, 이러한 기능이 100이 있다면? 하나의 파일에 넣어 정리해도, 그것을 매번 읽는 것은 쉽지 않을 것이다.
useReducer는 useState 기반 기능이 많을 때 효율적으로 분리하고 코드를 줄이기 위해 태어났습니다.
단순히 무작위 useReducer를 쓰면 코드가 짧아진다는 것보다 가독성과 코드의 작성 위치 등을 생각하고 구별해 가야 하는 것이 가장 중요하게 생각하는 점이 아닐까 생각한다.