본문 바로가기
Research/React

useReducer는 다재다능한 훅 + 커스텀훅

by RIEM 2023. 10. 25.

useReducer는 다양한 상황에서 컴포넌트 상태를 업데이트 해줄 수 있는 hook으로, useState보다 좀 더 다재다능한 훅이라 생각하면 된다.

useReducer는 첫 번째 파라미터에 reducer 함수를 두 번째 파라미터에 기본 값을 담은 객체를 넣어준다.

import { useReducer } from 'react';

function reducer(state, action) {
  // action.type에 따라 다른 작업 수행
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    default:
      return state;
  }
}

const Counter = () => {
  // const [value, setValue] = useState(0);
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <p>
        Value is <b> ${state.value}</b>
      </p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
    </div>
  );
};

export default Counter;

useState에서 setState를 썼던 것과 달리, useReducer에서 액션을 담당하는 dispatch 함수가 들어간다. 여기에 이미 reducer 함수 안에서 정의해둔 action.type에 매칭되는 type 프로퍼티를 객체에 넣어 dispatch 함수의 파라미터로 넣어준다.

 

useReduce로 Input 상태 관리하는 방법

useState로 input을 다룬다면 이렇게 하는데..

import { useEffect, useState } from 'react';

const Info = () => {
  const [name, setName] = useState('');
  const [nickname, setNickname] = useState('');

  useEffect(() => {
    console.log('effect');
    console.log(name);
    return () => {
      console.log('clean up');
      console.log(name);
    };
  }, [name]);

  const onChangeName = (e) => {
    setName(e.target.value);
  };

  const onChangeNickname = (e) => {
    setNickname(e.target.value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={onChangeName} />
        <input value={nickname} onChange={onChangeNickname} />
      </div>

      <div>
        <div>
          <b>Name: </b> {name}
        </div>
        <div>
          <b>Nickname: </b> {nickname}
        </div>
      </div>
    </div>
  );
};

export default Info;

 

useReducer을 사용하면

import { useReducer } from 'react';

function reducer(state, action) {
  // action.type에 따라 다른 작업 수행
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    default:
      return state;
  }
}

const Counter = () => {
  // const [value, setValue] = useState(0);
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <p>
        Value is <b> ${state.value}</b>
      </p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
    </div>
  );
};

export default Counter;

action type을 미리 정하고 이를 아래 onClick 이벤트 발생했을 때 dispatch라는 액션 함수에 action type을 기입해서 데이터를 업데이트해준다

 

Custom Hook으로 빼주기

useReducer 로직을 따로 커스텀훅으로 빼주는 것을 해보자. 그렇게 되면 다른 컴포넌트에서 재사용있게 해줄 수 있다.

// useInputs.js

import { useReducer } from 'react';

function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value,
  };
}

export default function useInputs(initialForm) {
  const [state, dispatch] = useReducer(reducer, initialForm);
  const onChange = (e) => {
    dispatch(e.target);
  };
  return [state, onChange];
}
// Info.js
import useInputs from './useInputs';

const Info = () => {
  const [state, onChange] = useInputs({
    name: '',
    nickname: '',
  });
  const { name, nickname } = state;

  return (
    <div>
      <div>
        <input name='name' value={name} onChange={onChange} />
        <input name='nickname' value={nickname} onChange={onChange} />
      </div>

      <div>
        <div>
          <b>Name: </b> {name}
        </div>
        <div>
          <b>Nickname: </b> {nickname}
        </div>
      </div>
    </div>
  );
};

export default Info;

 

댓글