useState vs useReducer

김동규·2023년 3월 1일

React 공부하기

목록 보기
7/10

개요

해당 글은 useState와 useReducer의 용례를 정리하기 위한 목적으로 서술한다.

공식적으로 useReducer 사용을 추천하는 경우

공식 문서의 기술에서는 다음과 같은 두 경우에서 useReducer의 사용을 추천한다.

  1. 다수의 sub-value를 가지는 복잡한 state를 다룰 때
  2. 다음 state가 이전 상태에 의존적일때

또한 useReducer는 콜백 함수대신 dispatch를 전달 할 수 있으므로 deep update를 트리거하는 컴포넌트의 성능을 최적화할 수 있습니다.

둘 다 useState로도 대체가 가능하기 때문에 구체적인 용례로 파악해보자.

다수의 sub-value를 가지는 복잡한 state를 다룰때

우선 useState라면....

  • 다루는 값이 여럿이라고 꼭 useState를 여러번 호출할 필요는 없다.
  • 또한 리액트는 변동된 State의 일부만 선택적으로 업데이트 할 수 있기 때문에
import { useState } from 'react'
import './App.css'

function App() {
  /* 이렇게가 아니라 */
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [age, setAge] = useState(0);

  /* 이렇게 선언해도 문제 없다. */
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: ''
  })

  /* 다수의 핸들러를 통한 선택적 업데이트의 예시 */
  const handleName = (e) => { 
    setUser({...user, name: e.target.value })
  }

  const handleEmail = (e) => { 
    setUser({...user, email: e.target.value })
  }

  const handleAge = (e) => { 
    setUser({...user, age: e.target.value })
  }
  
  /* 
  * 단일한 핸들러 함수로 처리한다면 
  * 문자열을 키값으로 가지는 객체의 특성을 이용해 
  * 이런 식으로 활용할 수도 있다.
  */
  const handleInput = (e) => {
  	setUser({...user, [e.target.name]: e.target.value})
  }

  return (
    <div className="App">
      <div>
        <input onChange={handle...} type="text" name="name"/>
        <input onChange={handle...} type="text" name="email"/>
        <input onChange={handle...} type="text" name="age"/>
        <button>제출</button>
      </div>
      <div>{user.name}</div>
      <div>{user.email}</div>
      <div>{user.age}</div>
    </div>
  )
}

export default App

useReducer의 경우...

import { useState, useReducer } from 'react'
import './App.css'

const initialState = {
    name: '',
    email: '',
    age: ''
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'name':
      return {
        ...state,
        name: action.payload
      };
    case 'email':
      return {
        ...state,
        email: action.payload
      };
    case 'age':
      return {
        ...state,
        age: action.payload
      };
    default:
      return initialState;
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div className="App">
      <div>
        <input onChange={(e) => dispatch({ type: 'name', payload: e.target.value })} type="text" />
        <input onChange={(e) => dispatch({ type: 'email', payload: e.target.value })} type="text" />
        <input onChange={(e) => dispatch({ type: 'name', payload: e.target.valueAsNumber })} type="text" />
        <button>제출</button>
      </div>
      <div>{user.name}</div>
      <div>{user.email}</div>
      <div>{user.age}</div>
    </div>
  )
}

export default App

다음 state가 이전 상태에 의존적일때

/* setState에 콜백 함수를 넘기면 해당 콜백의 매개변수는 자동적으로 이전 스테이트가 된다. */
setState((prev) => ...)
         
/* reducer 는 내부에 매개변수로 이전 state값을 받는다. */
const reducer = (state, action) => {
  switch (action.type) {
    case 'name':
      return {
        ...state,
        name: action.payload
      };
    case 'email':
      return {
        ...state,
        email: action.payload
      };
    case 'age':
      return {
        ...state,
        age: action.payload
      };
    default:
      return initialState;
  }
}

참고 링크

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

0개의 댓글