Sitemap

React hook — useReducer

6 min readDec 1, 2025

--

useReducer는 컴포넌트에 reducer를 추가하는 React Hook다.

useReducer는 컴포넌트의 상태 관리 로직을 더 구조적으로 다룰 때 사용하는 Hook이다.

useState와 비슷한 기능을 제공하지만, 더 구조적이라고 생각해도 좋을 것 같다. useState의 setState 함수를 외부 함수로 분리할 수 있다는 것이 가장 큰 특징이다.

1. Dispatch, Action, Reducer

Get XQ’s stories in your inbox

Join Medium for free to get updates from this writer.

useReducer는 Redux 패턴에서 온 아래 3가지 용어를 사용한다.

  • Dispatch: 상태(state) 변경 요청을 보내는 함수
  • Action: 상태(state)의 변경 로직을 적어놓은 객체
  • Reducer: 현재 상태(state)와 행동(action)을 받아서 새로운 상태를 반환하는 함수

레퍼런스

useReducer(reducer, initialArg, init?)

const [state, dispatch] = useReducer(reducer, initialArg, init?);

매개변수

  • reducer: state가 어떻게 업데이트 되는지 지정하는 Reducer 함수. Reducer 함수는 반드시 순수 함수여야 하며, State와 Action을 인수로 받아야 하고, 다음 State를 반환해야 한다. State와 Action에는 모든 데이터 타입이 할당될 수 있다.
  • initialArg: 초기 State가 계산되는 값이다. 모든 데이터 타입이 할당될 수 있다. 초기 State가 어떻게 계산되는지는 다음 init 인수에 따라 달라진다.
  • 선택사항 init: 초기 State를 반환하는 초기화 함수다. 이 함수가 인수에 할당되지 않으면 초기 State는 initialArg로 설정된다. 할당되었다면 초기 State는 init(initialArg)를 호출한 결과가 할당된다.

반환값

useReducer는 2개의 엘리먼트로 구성된 배열을 반환한다.

  1. 현재 state. 첫번째 렌더링에서의 state는 init(initialArg) 또는 initialArg로 설정된다 (init이 없을 경우 initialArg로 설정된다).
  2. dispatch 함수. dispatch는 state를 새로운 값으로 업데이트하고 리렌더링을 일으킨다.

주의 사항

  • useReducer는 Hook이므로 컴포넌트의 최상위 또는 커스텀 Hook에서만 호출할 수 있다. 반복문이나 조건문에서는 사용할 수 없다. 필요한 경우 새로운 컴포넌트를 추출하고 해당 컴포넌트로 State를 옮겨서 사용할 수 있다.
  • dispatch 함수는 안정된 식별성을 가지고 있기 때문에, 흔히 Effect의 의존성에서 제외하는 것을 볼 수 있다. 하지만 포함해도 Effect를 실행하지는 않는다. 린터에서 의존성을 생략해도 오류가 발생하지 않는다면, 그렇게 하는 것이 안전하다. Effect 의존성 제거에 대해 자세히 알아보자.
  • Strict Mode에서는 우연한 비순수성을 찾아내기 위해 Reducer와 init 함수를 두번 호출한다. 개발 환경에서만 한정된 동작이며, 배포(Production) 환경에는 영향을 미치지 않는다. Reducer와 init 함수가 순수 함수라면(그래야만 하듯이) 로직에 어떠한 영향도 미치지 않는다. 호출 중 하나의 결과는 무시한다.

실습

useReducer를 사용하여 counter 컴포넌트를 만들어 보자.

// reducer는 순수 함수
function reducer(state, action) {
// action.type에 따라 로직 분리
switch (action.type) {
case 'INCREMENT':
// payload 있을 시 payload 만큼 증가
return { count: state.count + (state.payload ?? 1) };
case 'DECREMENT':
// payload 있을 시 payload 만큼 감소
return { count: state.count - (state.payload ?? 1) };
case 'RESET':
// 초기화
return { count: 0 };
default:
// 알 수 없는 타입이 들어오면 기존 생태 반환
return state;
}
}

function Counter() {
// 초기값 설정: { count: 0 }
const [state, dispatch] = useReducer(reducer, { count: 0 });

return (
<div>
<p>count: {state.count}</p>

{/* dispatch를 통해 `Action`을 보냄 */}
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>

{/* payload를 통해 추가 데이터 전달 */}
<button onClick={() => dispatch({ type: 'DECREMENT', payload: 10 })}>
-10
</button>
<button onClick={() => dispatch({ type: 'INCREMENT', payload: 10 })}>
+10
</button>

<button onClick={() => dispatch({ type: 'RESET' })}>초기화</button>
</div>
);
}

주의

state는 읽기 전용이다. state의 객체나 배열을 변경하면 안 된다.

function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
// 🚩 Don't mutate an object in state like this:
state.count = state.count + 1;
return state;

대신 reducer에서 새로운 객체를 반환한다.

function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
// ✅ Instead, return a new object
return { count: state.count + 1 };

자세한 내용을 공부하려면 객체 State 업데이트하기와 배열 State 업데이트하기를 읽어보자.

--

--