본문 바로가기
React

리액트의 useReducer: 컴포넌트에서 상태변화 로직을 분리

by 글발 2024. 1. 3.
728x90
반응형

소개

리액트에서 상태 관리는 핵심적인 부분 중 하나입니다.

useState 훅을 통해 간단한 상태를 다루는 것도 좋지만,

복잡한 로직이나 여러 상태를 관리해야 하는 경우 useReducer 훅이 더 강력한 도구로 작용합니다.

이 글에서는 useReducer의 개념, 사용법, 그리고 실제 활용에 대해 간단히 알아보겠습니다.

1. useReducer란?

useReducer는 리액트에서 제공하는 특별한 훅으로,

상태와 액션을 사용하여 새로운 상태를 계산하는 함수입니다.

이를 통해 복잡한 상태 로직을 효과적으로 다룰 수 있습니다.

기본적인 사용법은 다음과 같습니다.

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

여기서 reducer는 상태를 어떻게 업데이트할지 정의한 함수이고,

dispatch를 통해 reducer를 호출할 수 있습니다.

initialState는 state의 초기값입니다.

2. useReducer의 사용법

2.1. 기본 사용

import React, { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

위 코드에서 볼 수 있듯이 컴포넌트로부터 상태변화 로직을 분리하였습니다.

reducer 함수는 액션의 타입에 따라 상태를 어떻게 업데이트할지 정의하고 있습니다.

dispatch 함수를 통해 액션을 전달하여 상태를 업데이트할 수 있습니다.

또한 좋은 장점이 하나 있는데

dispatch는 함수형 업데이트 그런거 필요없이
호출을 하면 알아서 현재의 state를 reducer 함수가 참조를 하므로
usecallback을 사용하면서 dependency array를 걱정하지 않아도 됩니다.

2.2. 복잡한 상태와 액션 로직

import React, { useReducer } from 'react';

const initialState = {
  todos: [],
  filter: 'all',
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map((todo) =>
          todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
        ),
      };
    case 'SET_FILTER':
      return { ...state, filter: action.payload };
    default:
      return state;
  }
};

const TodoApp = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <ul>
        {state.todos.map((todo) => (
          <li
            key={todo.id}
            style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
            onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
          >
            {todo.text}
          </li>
        ))}
      </ul>
      <input
        type="text"
        placeholder="Add Todo"
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            dispatch({ type: 'ADD_TODO', payload: { id: Date.now(), text: e.target.value, completed: false } });
            e.target.value = '';
          }
        }}
      />
      <div>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'all' })}>All</button>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'completed' })}>Completed</button>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'active' })}>Active</button>
      </div>
    </div>
  );
};

위 코드에서 reducerADD_TODO, TOGGLE_TODO, SET_FILTER 액션에 대한 로직을 처리하고 있습니다.

이렇게 useReducer를 통해 상태와 액션을 효과적으로 다룰 수 있습니다.

3. 주의사항

3.1. 복잡한 로직에만 사용

useReducer는 간단한 상태 관리에는 useState보다 무겁습니다.

복잡한 로직이나 여러 상태를 관리해야 할 때에만 사용하는 것이 좋습니다.

3.2. 불변성 유지

reducer 함수에서 상태를 업데이트할 때는 불변성을 유지하는 것이 중요합니다.

새로운 객체나 배열을 반환하여 상태를 업데이트하세요.

마무리

useReducer는 리액트에서 상태 관리를 위한 강력한 도구로,

복잡한 로직이나 여러 상태를 효과적으로 다룰 수 있습니다.

reducer 함수를 통해 액션에 따른 로직을 명확하게 정의하고,

여러 useReducer를 조합하여 전역 상태 관리에 활용할 수 있습니다.

주의사항을 잘 숙지하고 적절한 상황에서 사용하여 애플리케이션의 유지보수성과 확장성을 향상시켜보세요.