useReducer 개요

김동규·2023년 3월 1일

React 공부하기

목록 보기
8/10

개요

리액트는 상태관리를 위해 useState와 useReducer라는 두 종류의 훅을 제공한다. useReducer를 주제로 하는 글이지만 리액트에서 왜 같은 역할을 하는 훅을 두 종류나 제공하는지 의문이 들 수 있다. 기실 useState와 비교해보는 것이 useReducer의 이해에 도움이 되기도 한다.

따라서 본문에서는

  1. useReducer에 대해서 기술하고
  2. 두 훅의 차이점과 용례를 비교하고자 한다.

두 훅의 공통점과 차이점

두 훅의 생김새는 다음과 같다.

const [state, setState] = useState(initialValue);
const [state, dispatch] = useReducer(reducer, initialValue);

// 해당 글에서는 주제상 useReducer의 세 번째 매개변수 init은 무시한다.

이들의 공통점은 세 가지다.

  1. initialValue를 매개변수로 받아 초기 상태로 저장하고.
  2. 저장된 현재 상태(state)를 반환하며.
  3. 상태를 변경할 수 있는 수단(dispatch, setState)을 제공한다.

반면 차이점은 두 가지가 있다.

  1. 상태를 변경하는 함수의 이름이 다르다.(dispatch, setState)
  2. useReducer는 initialValue외에 reducer함수를 매개변수로 받는다.

이런 차이점들은 두 훅에서 상태를 변경하는 DataFlow가 다르기 때문에 발생한다.

DataFlow

시작하기에 앞서 상단에 게시된 gif는 리덕스 공식문서에서 리덕스 내부의 데이터 흐름을 가시화해둔 것이다. Reducer함수를 감싸고 있는 Store만 제외하면 useReducer훅에서 일어나는 데이터 흐름과 동일하다.

상세에 대해서는 차후 재조명할 예정이므로 지금은 한 번 살펴만 보고 넘기자.

useState의 DataFlow

일반적으로 useState의 DataFlow는 다음과 같다.

  1. 유저의 조작으로 이벤트가 발생한다.
  2. 발생한 이벤트를 이벤트 핸들러가 캐치한다.(onClick)
  3. 이벤트 핸들러에 전달된 콜백함수에서 setState함수를 호출하여 상태를 변경한다.
// 예시는 하단 참고링크 How to use React useReducer hook like a pro에서 발췌

const [state, setState] = useState(initialValue);

/*
* setState는 단순하게 기존의 state를 넘겨받은 매개변수(parameter)로 교체한다.
* 때문에 값을 산출하는 로직은 setCount( 매개변수 내부에 ) 작성되었다.
* prevCount => prevCount + 1은 값으로 평가할 수 있는 콜백 함수이므로
* setCount( number )와 동일하다.
*/
<button onClick={() => setCount(prevCount => prevCount + 1)}>
  +
</button>

useReducer의 DataFlow

반면 useReducer에는 한 단계가 추가된다.

  1. 유저의 조작으로 이벤트가 발생한다.
  2. 발생한 이벤트를 이벤트 핸들러가 캐치한다.(onClick)
  3. 이벤트 핸들러에 전달된 콜백함수에서 dispatch함수를 호출하여 reducer함수에게 type과 payload를 담은 액션객체를 전달한다.
  4. reducer함수는 내부에 정의된대로 action.type에 따라 상태를 변경한다.
const [state, dispatch] = useReducer(reducer, initialValue);
/*
* 값을 산출하고 state를 교체하는 로직은 모두 dispatch가 아니라 reducer함수가 담당한다. 
* 로직이 모두 reducer함수에 모여있기 때문에 보다 복잡한 state를 다루기에 유리하다.
*/
<button onClick={() => dispatch({ type: 'usename', payload: '홍길동' })}>
  +
</button>
  • dispatch
  • reducer 함수
  • 액션 객체 { type: '...', payload: '...' }

두 훅의 동작에 큰 차이점은 없지만 위와 같이 새로운 용어가 다수 등장하여 혼란스럽다. 일단 dispatch가 reducer함수에게 액션 객체를 전달하는 역할밖에 하지 않는다는 것은 이해할 수 있을 것이다.

그러므로 다음은 중요한 reducer함수와 액션 객체에 대해 살펴보자.

reducer 함수

reducer함수는 일반적으로 switch문을 이용하여 작성한다.

const reducer = (state, action) => {
  /*
  * ex : dispatch({ type: 'usename', payload: '홍길동' })
  * 1. 전달받은 액션객체에서 type을 확인한다. 
  * 2. action.type은 'username'이므로 해당 case를 작동시킨다. 
  * 3. 기존 state를 담고 username에 payload를 전달해 새로운 객체를 반환한다.
  *    (return {...state, username: action.payload })
  */
  switch (action.type) {
    case 'username':
      return { ...state, username: action.payload };
    case 'email':
      return { ...state, email: action.payload };
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
};

액션 객체

위에서 확인했다시피 액션 객체는 { type: '...', payload: '...' }형태의 객체다. 단, payload는 부가적인 요소로 요청의 특성에 따라 반드시 전달할 필요는 없다.
양 쪽 모두 일반적인 용례일 뿐 반드시 해당 이름일 필요는 없지만 리덕스의 경우 type필드를 필수적으로 요구하므로 특별한 이유가 없다면 따라주자.

아래는 useReducer훅의 전체 예시이다. 한 번 살펴보고 DataFlow의 그림을 다시 한 번 살펴보자.

// 하단 How to use React useReducer hook like a pro링크 발췌 

import { useReducer } from 'react';

const initialValue = {
  username: '',
  email: '',
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'username':
      return { ...state, username: action.payload };
    case 'email':
      return { ...state, email: action.payload };
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
};

const Form = () => {
  const [state, dispatch] = useReducer(reducer, initialValue);
  return (
    <div>
      <input
        value={state.username}
        onChange={(event) =>
          dispatch({ type: 'username', payload: event.target.value })
        }
      />
      <input
        value={state.email}
        onChange={(event) =>
          dispatch({ type: 'email', payload: event.target.value })
        }
      />
    </div>
  );
};

export default Form;

Wrappup

useReducer는

  1. 유저의 조작으로 이벤트가 발생하면
  2. 발생한 이벤트를 이벤트 핸들러가 캐치한다.(onClick)
  3. 이벤트 핸들러에 전달된 콜백함수에서 dispatch함수를 호출하여 reducer함수에게 type과 payload를 담은 액션객체를 전달한다.
  4. reducer함수는 내부에 정의된대로 action.type에 따라 상태를 변경한다.

참고

profile
공식문서를 사랑하는 프론트엔드 주니어 개발자

0개의 댓글