๐Ÿ” useReducer

๊น€์ˆ˜์ง„ยท2025๋…„ 7์›” 10์ผ

ํ•œ ์ž… ํฌ๊ธฐ๋กœ ์ž˜๋ผ ๋จน๋Š” ๋ฆฌ์•กํŠธ ์„น์…˜ 10 - useReducer

useReducer ๋ž€?

์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์ƒˆ๋กœ์šด State๋ฅผ ์ƒ์„ฑํ•˜๋Š” React Hook์ด๋‹ค.
๋ชจ๋“  useState๋Š” useReducer๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅํ•˜๋‹ค.
์ƒํƒœ ๊ด€๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

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]);
  };

์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์ƒํƒœ ๊ด€๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ๊ฒƒ

function reducer() {
//...
}

function App() {
	const [todos, dispatch] = useReducer(reducer);
    
    //...
}

useReducer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ์ƒํƒœ ๊ด€๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ค์Šต:

import { useReducer } from "react";

function reducer(state, action) {
  switch (action.tye) {
    case "INCREASE":
      return state + action.data;
    case "Decrease":
      return state - action.data;
    default:
      return state;
  }
}

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;

Exam.jsx์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค.
dispatch๋ž€? ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๋ฆฌ๊ณ  ๋ฐœ์†กํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

todolist ์ฝ”๋“œ์— ์ด์–ด์„œ ์‹ค์Šต

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

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 PopStateEvent.filter((item) => item.id !== action.targetId);
    default:
      return state;
  }
}

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

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

  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 todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
    </div>
  );
}

export default App;

๊ธฐ์กด ์ฝ”๋“œ์— Exam.jsx ๋ฅผ ๋งŒ๋“ค์–ด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๋ฉด useReducer๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ตฌํ˜„ ํ™”๋ฉด:

0๊ฐœ์˜ ๋Œ“๊ธ€