useReducer

곽지욱·2024년 9월 2일
0

React

목록 보기
6/12
post-thumbnail
  • useReducer()는 useState()와 같은 상태 관리, 상태 업데이트 훅이다.

  • 변경할 값이 많을 때, 즉 상태 관리할 데이터가 많아질 때 주로 많이 사용한다.

  • 좀 더 구조화된 방식으로 상태를 관리하고 싶을 때 사용할 수 있다.

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

  • state : 상태이름

  • dispatch : 상태를 변경할 때 필요한 정보를 전달하는 '함수'(주문서)

  • reducer : dispatch를 확인해서 state를 변경해주는 '함수'

  • initialState : state에 전달할 초기 값

서론

  • 리액트에서 상태 관리(업데이트)를 다루는 훅은 2가지 종류가 있다.

1.useState()

2.useReducer()

  • useReducer는 클래스 컴포넌트에서 setState와 메서드와 유사한 역할을 하지만 좀 더 구조화된 방식으로 상태를 관리할 수 있도록 도와준다.

선언

const [todos, dispatch] = useReducer(reducer, mockData);
  • todos state의 useReducer를 선언하였다. 초기 값으로 mockData를 넣어주었다.

function reducer(state, action) {
  switch (action.type) {
    case 'CREATE':
      return [action.data, ...state];
    case 'UPDATE':
      return state.map((item) =>
        item.id === action.targetId ? { ...item, isDone: !item.isDone } : item
      );
    case 'DELETE':
      return state.filter((item) => item.id !== action.targetId);
    default:
      return state;
  }
}
  • 위는 내가 작성한 Todo App의 reducer 함수 정의 부분이다

  • reducer 함수는 두 개의 인수를 받는다. state (현재 상태) 와 action 액션 객체

  • action.type 에 따라 다른 상태 변경 로직을 실행한다.

  • 예를 들면 CREATE 는 새 할일을 상태(state)에 추가하는 부분인데. action.type 이 CREATE로 들어오게 되면, 새로운 할 일을 기존의 state 배열 앞에 추가하고 새로운 배열을 반환한다.


그리고 onCreate 함수는 이렇게 작성하였다.

const onCreate = (content) => {
  dispatch({
    type: 'CREATE',
    data: {
      id: idRef.current++,
      isDone: false,
      content: content,
      date: new Date().getTime(),
    },
  });
};

새 할 일을 나타내는 객체를 생성하고 CREATE 액션과 함께 dispatch를 호출한다. dispatch 함수에 이렇게 타입과 data를 객체 형태로 선언할 수 있다.

  • 이때 dispatch에 전달되는 객체는 액션 객체로, type 속성과 데이터(data)를 포함하고 있습니다.

useReducer를 사용하는 이유

  1. useReducer를 사용한 코드 예시
import { useReducer } from 'react';

const mockData = [
  { id: 0, isDone: false, content: 'React 공부하기', date: new Date().getTime() },
  { id: 1, isDone: false, content: '빨래', date: new Date().getTime() },
  { id: 2, isDone: false, content: '운동', date: new Date().getTime() },
];

function reducer(state, action) {
  switch (action.type) {
    case 'CREATE':
      return [action.data, ...state];
    case 'UPDATE':
      return state.map((item) =>
        item.id === action.targetId ? { ...item, isDone: !item.isDone } : item
      );
    case 'DELETE':
      return state.filter((item) => item.id !== action.targetId);
    default:
      return state;
  }
}

function App() {
  const [todos, dispatch] = useReducer(reducer, mockData);

  const onCreate = (content) => {
    dispatch({
      type: 'CREATE',
      data: {
        id: Date.now(),
        isDone: false,
        content: content,
        date: new Date().getTime(),
      },
    });
  };

  const onUpdate = (targetId) => {
    dispatch({ type: 'UPDATE', targetId });
  };

  const onDelete = (targetId) => {
    dispatch({ type: 'DELETE', targetId });
  };

  return (
    <div>
      {/* UI Components here */}
    </div>
  );
}
  • 위 코드에서 useReducer를 사용함으로써 상태 관리 로직이 reducer 함수 하나에 집약되어 있으며, 상태 업데이트가 모두 dispatch 함수를 통해 일관되게 이루어진다는 것을 알 수 있다.

2. useReducer를 사용하지 않고 useState만 사용한다면?

import { useState, useRef } from 'react';

const mockData = [
  { id: 0, isDone: false, content: 'React 공부하기', date: new Date().getTime() },
  { id: 1, isDone: false, content: '빨래', date: new Date().getTime() },
  { id: 2, isDone: false, content: '운동', date: new Date().getTime() },
];

function App() {
  const [todos, setTodos] = useState(mockData);
  const idRef = useRef(3);

  const onCreate = (content) => {
    const newTodo = {
      id: idRef.current++,
      isDone: false,
      content: content,
      date: new Date().getTime(),
    };
    setTodos([newTodo, ...todos]);
  };

  const onUpdate = (targetId) => {
    setTodos(
      todos.map((item) =>
        item.id === targetId ? { ...item, isDone: !item.isDone } : item
      )
    );
  };

  const onDelete = (targetId) => {
    setTodos(todos.filter((item) => item.id !== targetId));
  };

  return (
    <div>
      {/* UI Components here */}
    </div>
  );
}

useReducer와 useState 비교

  • 코드 분리와 가독성 측면

  • useReducer : redecer 함수에 모든 상태 변경 로직이 정의되어 있다, 이로 인해 상태 관리 로직이 한 곳에 집중되어 있으며, 상태가 어떻게 변경되는지를 쉽게 파악할 수 있다. 또한 상태 업데이트가 모두 dispatch 를 통해 일관되게 이루어지므로 코드 가독성과 유지보수성이 높아진다.

  • useState: 상태 변경 로직이 onCreate, onUpdate, onDelete 함수 내부에 직접 구현되어 있다. 상태 변경 로직이 분산되어 있어, 시간이 지남에 따라 코드의 복잡성이 증가하고 유지보수가 어려워질 수 있다.

  • 상태 업데이트 방식

  • useReducer: dispatch를 사용하여 상태 업데이트 요청을 보냅니다. 이 방식은 상태 변경을 명확하고 예측 가능하게 만들어 줍니다. 또한, action 객체를 사용하여 상태 변경에 필요한 추가적인 정보를 전달할 수 있습니다.

  • useState: 상태를 직접 변경하기 위해 setTodos를 호출합니다. 이 방식은 간단한 상태 변경에는 적합하지만, 복잡한 상태 변경 로직이 필요한 경우 코드가 지저분해질 수 있습니다.

0개의 댓글