useReducer란?
상태를 관리하고 업데이트하는 훅인 useState의 대체 함수.
useState의 업그레이드 버전이라고 생각하면 된다.
다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 useState가 아닌 useReducer 사용을 통해 컴포넌트의 성능을 최적화시킬 수 있다고 한다.(callback 대신 dispatch를 전달하기 때문)
useReducer는 다음과 같이 사용된다.
const [state, dispatch] = useReducer(reducer, initialArg, init);
state
현재 상태 값. useReducer로 관리되는 상태의 현재 값이며, reducer 함수의 반환 값에 의해 업데이트 된다.
초기 상태는 initialArg 또는 init 함수의 반환값으로 설정된다.
const [state, dispatch] = useReducer(reducer, { count: 0 });
console.log(state);
dispatch
상태를 업데이트해주는 함수. action 객체를 reducer에 전달하여 상태 변경한다.
dispatch({ type: "INCREMENT" });
reducer
상태 업데이트 로직을 정의하는 함수.
state와 action을 인자값으로 받고 새로운 상태 값을 계산한 후 반환한다.
const reducer = (state, action) => {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
default:
return state;
}
};
initialArg
상태의 초기 값.
init(optional)
초기 상태를 동적으로 계산하기 위한 함수. initialArg의 값을 입력 받아 초기 상태를 반환.
const init = (initialState) => {
return { count: initialState.count * 2 };
};
const [state, dispatch] = useReducer(reducer, { count: 5 }, init);
console.log(state); // { count: 10 }
그래서 useState와 차이점은 뭔데?
state를 업데이트 하는 로직을 컴포넌트 내부에서 하는 방식인지, 밖으로 따로 분리해서 불러오기만 하는 방식인지의 차이다.
useState
useState는 컴포넌트 내부에 state를 업데이트하는 로직이 있다.
const Counter = () => {
const [count, setCount] = useState(0);
const increase = () => {
setCount(count + 1);
};
const decrease = () => {
setCount(count - 1);
};
const reset = () => {
setCount(0);
};
return (
<>
<p>카운트: {count}</p>
<button onClick={increase}>증가</button>
<button onClick={decrease}>감소</button>
<button onClick={reset}>초기화</button>
</>
);
};
useReducer
useReducer는 컴포넌트 외부에 state 업데이트 로직을 둘 수 있다.
const reducer = (state, action) => {
switch (action.type) {
case "INCREMENT":
return state + action.data;
case "DECREMENT":
return state + action.data;
case "RESET":
return 0;
default:
return state;
}
};
const Counter = () => {
// dispatch - 상태를 변경하는 트리거 함수
const [count, dispatch] = useReducer(reducer, 0);
return (
<>
<p>카운트: {count}</p>
<button
onClick={() => {
dispatch({ type: "INCREMENT", data: 1 });
}}
>
증가
</button>
<button
onClick={() => {
dispatch({ type: "DECREMENT", data: -1 });
}}
>
감소
</button>
<button
onClick={() => {
dispatch({ type: "RESET" });
}}
>
초기화
</button>
</>
);
};
위 예시로 봤을 땐 정확히 useReducer가 뭐가 좋은건지 감이 잘 안 잡힐 수 있다.
하지만 업데이트 로직이 복잡한 로직이라면?
복잡한 로직을 한 컴포넌트 내부에서 일일이 작성한다면 코드의 가독성이 떨어지고 더 깔끔한 방법을 찾고 싶을 것이다.
이 복잡한 상태 변경 로직을 reducer로 추상화시켜서 외부 다른 파일로 분리해서 필요할 때마다 꺼내서 써온다면 훨씬 깔끔하게 코드를 작성할 수 있을 것이다.
구분 | useState | useReducer |
상태의 복잡성 | 단순한 상태 관리에 적합 (예: 카운터, 폼 입력값 등) |
상태 로직이 복잡하거나 여러 상태가 의존적으로 연결되어 있을 때 사용 (예: 여러 액션을 처리하는 상태) |
상태 변경 로직의 구조화 | 상태 변경 로직이 간단하거나 한두 줄로 처리 가능한 경우 |
상태 변경 로직을 하나의 reducer 함수로 구조화하여 관리해야 할 경우 |
상태의 개수 | 상태가 독립적이고 개수가 적은 경우 (예: count, isOpen 등) |
상태가 많고 서로 의존적이거나 하나의 state 객체로 관리해야 하는 경우 (예: 폼 상태, 체크박스 그룹 상태 등) |
외부 도구와의 통합 | Context API가 필요하지 않거나 상태를 지역적으로 관리하는 경우 |
Context API와 함께 사용하여 전역 상태를 관리하거나 props drilling 문제를 해결할 때 |
예제 | 1. 간단한 카운터 2. 폼 입력 필드 관리 3. 모달 열림/닫힘 관리 |
1. 복잡한 폼 상태 관리(예: 여러 단계의 입력 상태 관리) 2. Todo 리스트 상태 관리 3. API 요청 상태 관리 (로딩, 성공, 실패) |
'React' 카테고리의 다른 글
[React] Portal 활용하여 모달 만들기 (0) | 2025.01.19 |
---|---|
[React] Context API 개념 및 사용 방법 정리 (1) | 2024.11.26 |
[React] ReactNode / ReactElement / JSX.Element 의 차이 (0) | 2024.06.25 |
[React / Recoil] Link 태그와 a 태그의 차이(userState가 undefined가 생기는 문제) (0) | 2023.06.19 |