[React] UseReducer

Jinny·2023년 11월 7일
1

React

목록 보기
3/23

1. useReducer

리액트에서 상태를 업데이트할 때 새로운 배열 또는 객체로 반환해야 한다.

useReducer를 사용하면 복잡한 객체를 추가/삭제/수정하는 코드를 다른 파일에 보관해 코드를 재사용할 수 있다!

기존에 useState로 상태값을 업데이트할 때 새로운 배열을
만들어 반환하는 모습이다.

import { useState } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';

export default function TaskApp() {
  const [tasks, setTasks] = useState(initialTasks);

  function handleAddTask(text) {
    setTasks([
      ...tasks,
      {
        id: nextId++,
        text: text,
        done: false,
      },
    ]);
  }

  function handleChangeTask(task) {
    setTasks(
      tasks.map((t) => {
        if (t.id === task.id) return task;
        return t;
      })
    );
  }

  function handleDeleteTask(taskId) {
    setTasks(tasks.filter((t) => t.id !== taskId));
  }

  return (
    <>
      <h1>Prague itinerary</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

let nextId = 3;
const initialTasks = [
  {id: 0, text: 'Visit Kafka Museum', done: true},
  {id: 1, text: 'Watch a puppet show', done: false},
  {id: 2, text: 'Lennon Wall pic', done: false},
];
  • 상태를 업데이트하기 위해 setTasks를 호출하고
    복잡한 데이터일수록 새롭게 반환하는 상태 데이터의 양도 많아지기 때문에 보기 안좋다.

➡️ 상태를 업데이트하는 로직을 컴포넌트 밖으로 빼오면 유지 및 관리하기가 좋으며 다른 컴포넌트에서도 재사용할 수 있다!

2. useReducer 사용법

💡 <useState에서 useReducer로 변경하는 방법>

  1. 이벤트 핸들러에 등록된 함수가 무슨 상태를 원하는 것인지 파악한다.
function handleAddTask(text) {
  setTasks([
    ...tasks,
    {
      id: nextId++,
      text: text,
      done: false,
    },
  ]);
}

function handleChangeTask(task) {
  setTasks(
    tasks.map((t) => {
      if (t.id === task.id) {
        return task;
      } else {
        return t;
      }
    })
  );
}

function handleDeleteTask(taskId) {
  setTasks(tasks.filter((t) => t.id !== taskId));
}
``
  • Reducer는 상태 관리할 때 쓰이며 상태를 직접 설정하는 것과 약간 다르다.
  • 리액트에게 "무엇을 하라고" 알려주는 대신, 이벤트 핸들러에서 "액션"을 전달하여 사용자가 수행할 작업을 지정한다.
  • 위 코드의 경우, 업무를 추가/삭제/변경하는 로직이 담겨있다.setTasks 처럼 작업 상태를 설정하지 않고
    dispatch를 통해 작업 추가/삭제/변경을 한다는 액션을 보낸다.
  1. 상태를 관리하는 파일(task-reducer.js)를 따로 만든다.
export default function tasksReducer(tasks, action) {
  switch(action.type){
   	 case 'added': {
    	const {id,text} = action;
    	return [
     	 ...tasks,
      	{
       	 id,
         text,
         done: false,
        },
       ];
   }
    case 'changed': {
    	const {task} = action;
    	return tasks.map((t) => {
       		if (t.id === action.task.id)  return task;
      	   return t;
    	});
    } 
    case 'deleted': {
    	const {id} = action;
   		 return tasks.filter((t) => t.id !== id);
    }
     default: {
    	throw Error(`Unknown action:${action.type}`);
      }
}
  • 기존의 tasks 데이터를 받아오고 무슨 행동을 원하는지에 대한 정보 action을 받아온다. 이때 액션 타입은 dispatch를 통해 우리가 따로 지정한다.
  • 전달받은 tasks 데이터를 추가/수정/삭제에 따라
    새로운 tasks 배열을 리턴하는 함수를 만든다.
    (이전에 setTasks에 정의한 코드 이용)
  • 만약 해당되지 않는 action이라면 default 문을 통해
    에러를 던져준다.
  1. 컴포넌트에 useReducer를 추가한다.

useReducer를 사용할 때 초기 상태값(intialTasks)과 새로운 상태를 만들어 나갈 함수(tasksReducer)를 전달한다.

import { useReducer } from 'react';
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

function handleAddTask(text) {
    dispatch({type:'added',id: nextId++,text: text});
}

function handleChangeTask(task) {
    dispatch({type: 'changed',task: task});
 }

 function handleDeleteTask(taskId) {
    dispatch({ type: 'deleted',id: taskId});
 }
  • 상태에 접근할 수 있는 변수 taskstasksReducer함수를 호출할 수 있는 dispatch를 통해 우리가 원하는 액션을 명령할 수 있다.
  • dispatch에는 액션 객체로 무엇을 원하는지와 업데이트 시 필요한 데이터를 전달한다.

예를 들어, dispatch({type:'added',id: nextId++,text: text}); 호출하면 useReducer가 자동으로 tasksReducer 함수를 호출한다. 이때 추가 시 필요한 데이터 id와 text 데이터도 같이 전달한다.

상태 관리하는 곳에서 action 객체를 통해 필요한 데이터를 받아와 기존의 tasks 데이터에서 새로운 데이터를 추가할 수 있게 된다.

0개의 댓글