본문 바로가기
Coding/[Web] Frontend

[React] useReducer

by Gofo 2023. 12. 24.

useReducer

React에서 제공하는 훅 중 useReducer라는 상태 관리 훅이 있다.

 

useState와 마찬가지로 상태를 관리하지만,
컴포넌트로부터 로직을 분리하기 때문에 관리해야하는 상태나 로직이 복잡한 상황에서 useState보다 컴포넌트를 좀 더 깔끔히 관리할 수 있다는 장점이 있다.

 

import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...
}

 

Parameter, Return

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

 

위와 같이 useReducer에는 3개의 파라미터를 넘겨줄 수 있다.

  • reducer
    • required
    • 상태를 업데이트하는 함수
    • <pre>(state, action) => {...} </pre>
  • initialArg
    • required
    • 초기 상태값
  • init
    • optional
    • 초기 상태를 정의하는 함수 : 2번째 인자인 initalArg를 파라미터로 받는 함수이다.
    • <pre>(initialArg) => {...}</pre>

 


Reducer

Reducer는 상태를 업데이트하는 함수로,
dispatch를 호출하면 dispatch의 인자가 reducer의 action으로 넘어가며 호출된다.

아래와 같이 생각하면 편하다.

function dispatch(action) {
  reducer(this.state, action)
}

 

파라미터

Reducer는 2개의 파라미터를 가진다.

  • state : 현재 상태
  • action: 사용자에 의해 수행되는 액션으로, dispath 함수를 호출할 때 넘기는 인자이다.
    • <pre>dispath({type: 'age', age: 10})</pre> 를 통해 dispatch 함수를 호출하는 경우
      <pre>{type: 'age', age: 10}</pre>이 action이다.
    • 어떤 형식의 타입이든 올 수 있지만, 컨벤션은 type을 같이 넘겨서 어떤 종류의 행위인지 명시해주는 것이다.

 

Return

Reducer는 바꾸고자하는 상태를 return 해야 한다.

 

Reducer를 생성할 때는 아래 두가지 사항을 주의해야한다.

  • 첫번째 인자인 state는 readonly이므로 수정하지 말고 새로운 object를 return 할 것
  • 여러 key를 가지는 object인 경우 변경하는 대상 외의 값들을 누락하지 않도록 주의할 것
  • reducer는 pure 함수여야 한다.

 

주의사항 1

첫번째 인자인 state는 readonly 이기 때문에 수정하기보다는 새로운 object를 만들어서 return 해야 한다.

 

🙅‍♂️ 안좋은 방법

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

 

🙆‍♂️ 좋은 방법

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

 

주의사항2

여러 key를 가지는 object인 경우 다른 값들에 대해 영향을 미칠 수 있음을 고려해야 한다.

 

<pre>{a: ~~, b: ~~, c: ~~}</pre> 형태의 obejct가 있는 경우,

reducer 내에서 <pre>return {a: 10}</pre> 을 하게 된다면 상태는 b와 c가 없는 a만 존재하는 object로 변화하게 된다.

때문에 <pre>return {...state, a: 10}</pre> 과 같이 spread 연산자를 써서 다른 값들에 대해서도 유지해야한다.

 

초기값이<pre>{age: 10, name: '홍길동'}</pre> 라는 상태가 있을 때 age를 20으로 바꿔보자.

그를 위해 <pre>dispatch({type: 'age', age: 20})</pre>를 호출한다.

 

그러면 reducer의 두번째 인자로 object가 넘어온다.

그걸 받아서 바꾸고자하는 새로운 상태값을 return 해주면 된다.

아래 4번 줄 처럼 return 시에는 기존 state의 다른 값(name)이 누락되지 않도록 고려해야한다.

function reducer(state, action) {
  switch (action.type) {
    case 'age':
      return { ...state, age: action.age };
    default:
      return state;
  }
}

 

 

주의사항3

Reducer는 pure 해야하며, 비동기 함수는 원칙적으로는 받지 않는다.

 

상태관리 시 비동기 로직이 필요한 경우가 있다. 이를 위해 서칭 중 괜찮은 글을 찾았다.

필요 시 아래 글처럼 커스텀 훅을 만들거나 아래 글에서 제시해주는 패턴에 따라 코드를 작성하면 될 듯하다.

 

https://velog.io/@allenk/useReducer로-비동기-로직-다루기

 

useReducer로 비동기 로직 다루기

useReducer로 비동기 로직을 다루는 방법을 소개합니다.

velog.io

 


참고

 

 

 

 

댓글