개발새발개발/React

[React] useReducer 사용하기

birdsfoot 2025. 2. 20.

 

 

useReducer

  • 컴포넌트 내부에 새로운 State를 생성하는 React Hook
  • 모든 useState는 useReducer로 대체 가능
  • useState는 state를 관리하는 코드를 컴포넌트 내부에만 작성 가능
  • 하지만, useReducer를 사용하면 state를 관리하는 코드를 컴포넌트 외부에 작성할 수 있어 코드의 가독성을 높일 수 있다

 

 

사용하기

import { act, useReducer } from 'react';

const reducer = (state, action) => {
    if (action.type === 'INCREASE') {
      return state + action.data;
    } else if (action.type === 'DECREASE') {
      return state - action.data;
    }
};

const Exam = () => {
  const [state, dispatch] = useReducer(reducer, 0);

  const onClickPlus = () => {
    dispatch({
      type: 'INCREASE',
      data: 1,
    });
  };

  const onClickMinus = () => {
    dispatch({
      type: 'DECREASE',
      data: 1,
    });
  };
  return (
    <div>
      <h1>{state}</h1>
      <button onClick={onClickPlus}>+</button>
      <button onClick={onClickMinus}>-</button>
    </div>
  );
};

export default Exam;

 

 

 

1. useReducer 선언

const Exam = () => {
  const [state, dispatch] = useReducer(reducer, 0);

 

  • state의 이름은 todo 등 원하는 것으로 해도 됨
  • 뒤에는 reducer 함수와 기본값을 받음(기본값 생략 가능)
  • reducer는 컴포넌트 외부에 따로 선언해줌(아래에서 자세히)
  • useReducer는 아래와 같이 import 해서 가져와야 함
import { useReducer } from 'react';

 

 

 

2. dispatch 함수 생성

  const onClickPlus = () => {
    dispatch({
      type: 'INCREASE',
      data: 1,
    });
  };

  const onClickMinus = () => {
    dispatch({
      type: 'DECREASE',
      data: 1,
    });
  };

 

  • 그 뒤 함수에 dispatch 를 이용해 type과 data의 값을 넘겨줌
  • data의 값이 여러개일 경우 객체({  }) 형태로 전달

 

3. 이벤트 핸들러 부착

  return (
    <div>
      <h1>{state}</h1>
      <button onClick={onClickPlus}>+</button>
      <button onClick={onClickMinus}>-</button>
    </div>
  );

 

  • onClick 이벤트 핸들러에 생성한 함수 부착

 

4. reducer 설정

const reducer = (state, action) => {
  if (action.type === 'INCREASE') {
    return state + action.data;
  } else if (action.type === 'DECREASE') {
    return state - action.data;
  }
};

 

  • 컴포넌트 외부에 reducer 함수 생성
  • state와 action 을 값으로 받음
  • state는 현상태인거고, action은 수정된 값(setState 느낌)
  • if와 else if를 이용해 type에 맞게 동작을 정해줌
  • return을 사용해서 넘겨줌!

 

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREASE':
      return state + action.data;
    case 'DECREASE':
      return state - action.data;
    default:
      return state;
  }
};

 

  • 조건이 많을 경우 if문보다 switch와 case를 더 많이 사용함
  • type별로 작성하고 default를 이용해 기본값을 넘겨줌

 

 

TodoList App 을 useReducer로 수정하기

 

1. onCreate

import { useState, useRef, useReducer } from 'react';
import './App.css';
import Editor from './components/Editor';
import Header from './components/Header';
import List from './components/List';
import Exam from './components/Exam';

const mockTodo = [...];

function reducer(state, action) {
  switch (action.type) {
    case 'CREATE':
      return [action.data, ...state];
    default:
      return state;
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, mockTodo);
  const idRef = useRef(3);

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


  return (
    <div className="App">
      <Header />
      <Editor onCreate={onCreate} />
      <List todo={state} onUpdate={onUpdate} onDelete={onDelete} />
    </div>
  );
}

export default App;

 

  • 기존에 넘겨주던 값을 onCreate에 dispatch를 붙여 data에 담아서 보내줌
  • dispatch({}) 괄호 주의하기! 여러개니까 객체로 줘야함

 

2. onUpdate, onDelete

import { useState, useRef, useReducer } from 'react';
(...)

const mockTodo = [...];

function reducer(state, action) {
  switch (action.type) {
    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 [state, dispatch] = useReducer(reducer, mockTodo);
  const idRef = useRef(3);

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

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

  return (
    <div className="App">
      <Header />
      <Editor onCreate={onCreate} />
      <List todo={state} onUpdate={onUpdate} onDelete={onDelete} />
    </div>
  );
}

export default App;

 

  • Update와 Delete에는 targetId만 필요하므로 그 값만 넘겨줌
  • 자세한 state 관리는 컴포넌트 외부의 reducer에서 처리함
  • 매개변수로 전달된 값은 action에 들어있음

 

 

 

댓글