useReducer는 State(상태)를 관리하고 업데이트하는 Hook에만 useState를 대체할 수 있는 Hook. 즉, useState처럼 State를 관리하고 업데이트할 수 있다.
useReducer는 한 컴포넌트 내에서 State를 업데이트하는 로직 부분을 그 컴포넌트로부터 분리시키는 것이 가능하다.
상태와 액션(action)을 받아서, 새로운 상태를 반환하는 리듀서 함수를 이용한다.
그러므로, useReducer는 다음과 같은 상황에서 사용될 수 있다.
복잡한 상태 로직을 다룰 때
여러 개의 연관된 상태를 함께 다룰 때
컴포넌트의 상태 업데이트 로직을 다른 파일로 분리하고 싶을 때
useReducer 함수는 첫 번째 인자인 reducer 함수가 반환(return) 하는 값으로 state를 갱신하는 역할을 합니다.
const [state, dispatch] = useReducer(reducer, initialState, init);
state : 컴포넌트에서 사용할 상태
dispatch : reducer 함수를 실행시키며, 컴포넌트 내에서 state의 업데이트를 일으키기 위해서 사용하는 함수
reducer : 컴포넌트 외부에서 state를 업데이트하는 로직을 담당하는 함수. 현재의 state와 action 객체를 인자로 받아서, 기존의 state를 대체할 새로운 State를 반환하는 함수.
initialState : 초기 State
init : 초기 함수 (2번째 인자인 initialState를 파라미터로 받는 함수)
dispatch 함수는 reducer 함수를 실행시킨다. dispatch 함수의 인자로써 업데이트를 위한 정보를 가진 action을 이용하며, 컴포넌트 내에서 state의 업데이트를 일으키기 위해 사용된다.
action은 업데이트를 위한 정보를 가지고 있는 것이며, reducer 함수의 두 번째 인자인 action에 할당된다. action은 정해진 형태는 없으나 주로 type라는 값을 지닌 객체 형태로 사용된다. (type 속 action(값)은 대문자와 '_'를 사용한다.)
dispatch({ type: "DECREMENT" })
<button onClick={() => dispatch({ type: "INCREMENT" })}>증가</button>
다음과 같이 데이터를 정의하여 action을 사용할 수도 있다.
<button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>증가</button>
reducer 함수는 dispatch 함수에 의해 실행되며, 컴포넌트 외부에서 state를 업데이트하는 로직을 담당한다. reducer 함수는 현재의 state와 action은 인자로 받게 되는데, 이 action 값에 근거하여 기존의 state를 대체할 새로운 state를 반환한다.
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
throw new Error("unsupported action type: ", action.type);
}
}
reducer 함수는 기존의 state를 새로운 state로 대체하는 것이다. 즉, 기존의 state를 변경하거나, 추가하거나, 덮어쓰지 않는다.
여러 key를 가지는 object인 경우 다른 값들에 대해 영향을 미칠 수 있음을 고려해야 한다.
예를 들어 {a: ~~, b: ~~, c: ~~} 형태의 obejct가 있는 경우, reducer 내에서 return {a: 10} 을 하게 된다면 상태는 b와 c가 없는 a만 존재하는 object로 변화하게 된다. return {...state, a: 10} 과 같이 spread 연산자를 써서 다른 값들에 대해서도 유지해주어야 한다.
마지막으로 Reducer는 pure해야한다. 비동기 함수는 원칙적으로는 받지 않는다.
useState는 간단한 상태 로직을 다루기에 적합하고, useReducer는 좀 더 복잡한 상태 로직을 다루기에 적합하다.
useState는 상태를 업데이트할 때 이전 상태를 덮어쓰지만, useReducer는 이전 상태를 변경하지 않고 새로운 상태를 생성한다.
useState는 한 개의 상태만 다룰 수 있지만, useReducer는 여러 개의 연관된 상태를 함께 다룰 수 있다.
useState는 컴포넌트 내부에서 상태를 다루지만, useReducer는 다른 파일에서 reducer 함수를 정의하여 상태를 다룰 수 있다.