DEVELOPMENT/react

11장 useReducer와 상태 관리

Tiny Commit 2025. 1. 6. 10:07

1. useReducer 이해하기

리액트 훅 useReducer를 이용하면 컴포넌트에서 상태 변화 코드를 쉽게 분히 할 수 있습니다. 

 

1. 실습 준비하기 

// 2025-01-06 7장 useReducer와 상태관리리
// src/App.js
// 1.1.1 useReducer이해하기기
import "./App.css";
import Header from "./component/Header";
import TodoEditor from "./component/TodoEditor";
import TodoList from "./component/TodoList";
import { useState, useRef } from "react";
import TestComp from "./component/TestComp";


const mockTodo = [ 
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    createdDate: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래 널기",
    createdDate: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래 연습하기",
    createdDate: new Date().getTime(),
  },
];

function App() {
  const idRef = useRef(3);
  const [todo, setTodo] = useState(mockTodo);

  const onCreate = (content) => {
    const newItem = {
      id: idRef.current, 
      content,
      isDone: false,
      createdDate: new Date().getTime(),
    };
    setTodo([newItem, ...todo]);
    idRef.current += 1; 
  };
  const onUpdate = (targetId) => { 
    setTodo(
      todo.map((it) =>
        it.id === targetId ? { ...it, isDone: !it.isDone } : it
      )
    );
};
  const onDelete = (targetId) => { 
    setTodo(todo.filter((it) => it.id !== targetId));
  };

  return (
    <div className="App">
      <TestComp />
      <Header />
      <TodoEditor onCreate={onCreate} />
      <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} /> 
    </div>
  );
}
export default App;

 

// 2025-01-06 7장 useReducer와 상태관리리
// src/component/TestComp.js
// 1.1.1 useReducer이해하기기
import { useState } from "react";

function TestComp(){
    const [count, setCount] = useState(0);

    const onIncrease = () => {
        setCount(count + 1);
    };

    const onDecrease = () => {
        setCount(count - 1);
    };

    return (
        <div>
            <h4> 테스트 컴포넌트 </h4>
            <div>
                <bold>{count}</bold>
            </div>
            <div>
                <button onClick = {onIncrease}>+</button>
                <button onClick = {onDecrease}>-</button>
            </div>
        </div>
    )
}
export default TestComp;

 

 

 

2. 상태 변환 코드란?

상태변환 코드란 State값을 변경하는 코드입니다. 

(앞에 만든 onIncrease와 onDecrease가 상태변환 코드라고 할 수 있습니다.)

 

컨포넌트에서 상태변환코드를 분리한가는 것은 컨포넌트 내부에 작성한 코드를 외부에 작성한다는 뜻입니다.

useState를 이용해 만들면 상태 변환 코드를 분리 할 수 없습니다. 때문에 useReducer을 이용해서 외부에 작성합니다.

 

 

 

3. useReducer의 기본 사용법 

useReducer은 리액트 컨포넌트에서 State를 관리하는 리액트의 훅입니다. 

State관리를 컨포넌트 외부에서 할 수 있기 때문에 상태변화 코드를 분리 할 수 있습니다. 

파일로 분리가 가능하기 때문에 컴포넌트 내부가 훨씬 간결해 집니다.

 

// src/component/TestComp.js
// 1.3.1 useREducer의 기본 사용법
import { useReducer } from "react";

function reducer(state, action) {
    switch (action.type){
        case "INCREASE":
            return state + action.data;
        case "DECREASE":
            return state - action.data;
        case "INIT":
            return 0;
        default:
            return state;
    }
}

function TestComp(){
    const [count, dispatch] = useReducer(reducer ,0);
    // dispatch: 상태변화가 필요할 때 촉발하는 함수, dispatch에서 인수로 전달되는 값에 State변화 값이 들어 있고 이를 action객체라고 합니다.
    // dispatch가 실행되면 함수 reducer이 실행되는데, 이 함수가 반환하는 값이 새로운 State값이 됩니다.
    // dispatch 호출 -> reducer호출 -> State값 업데이트트

    return (
        <div>
            <h4> 테스트 컴포넌트 </h4>
            <div>
                <bold>{count}</bold>
            </div>
            <div>
                <button onClick={() => dispatch({type: "INCREASE", data: 1 })}>
                    +
                </button>
                <button onClick={() => dispatch({type: "DECREASE", data: 1 })}>
                    -
                </button>
                <button onClick={() => dispatch({type: "INIT"})}>
                    0으로 초기화
                </button>
            </div>
        </div>
    )
}
export default TestComp;

 

 

 

 

 

 

 

 

 

2. [ 할 일 관리 ]앱 업그레이드

1. useState를 useReducer로 바꾸기

실무에서는 State가 복작하지 않으면 useState를 사용하지만, 그렇지 않으면 useReducer을 사용합니다.

 

useState를 삭제합니다. (아직 실행x)

// src/App.js
// 2.1 할일 관리 앱 업테이트
import "./App.css";
import Header from "./component/Header";
import TodoEditor from "./component/TodoEditor";
import TodoList from "./component/TodoList";
import { useReducer, useRef } from "react";
import TestComp from "./component/TestComp";


const mockTodo = [ 
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    createdDate: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래 널기",
    createdDate: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래 연습하기",
    createdDate: new Date().getTime(),
  },
];

function useReducer(state, action) {
  return state;
}

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

  const onCreate = (content) => {
    idRef.current += 1; 
  };
  const onUpdate = (targetId) => { 
};
  const onDelete = (targetId) => { 
  };

  return (
    <div className="App">
      <TestComp />
      <Header />
      <TodoEditor onCreate={onCreate} />
      <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} /> 
    </div>
  );
}
export default App;

 

 

 

 

2. Create: 할일 아이템 추가하기

3. Update: 할 일 아이템 수정하기 

4. Delete: 할 일 삭제 구현하기

onCreate에서 dispatch를 호출하고, 인수로 할 일 정보를 담은 action객체를 전달합니다.

 

// src/App.js
// 2.3 Update
import "./App.css";
import Header from "./component/Header";
import TodoEditor from "./component/TodoEditor";
import TodoList from "./component/TodoList";
import { useReducer, useRef } from "react";


const mockTodo = [ 
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    createdDate: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래 널기",
    createdDate: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래 연습하기",
    createdDate: new Date().getTime(),
  },
];

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

function App() {

  const [todo, dispatch] = useReducer(reducer, mockTodo);
  const idRef = useRef(3);

  const onCreate = (content) => {
    dispatch({
      type: "CREATE",
      newItem: {
        id: idRef.current,
        content,
        isDone: false,
        createDate: new Date().getTime(),
      },
    })
    idRef.current += 1; 
  };
  const onUpdate = (targetId) => { 
    dispatch({
      type: "UPDATE",
      targetId,
    });
};
  const onDelete = (targetId) => { 
    dispatch({
      type: "DELETE",
      targetId,
    });
  };

  return (
    <div className="App">
      <Header />
      <TodoEditor onCreate={onCreate} />
      <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} /> 
    </div>
  );
}
export default App;

 

 

 

 

 

 

 

 


 


출처 :  이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).