useState를 useReducer로 바꿔보기

Doozuu·2025년 3월 20일
0

React

목록 보기
27/30
post-thumbnail

1. setState를 dispatch 함수로 바꾸기

dispatch 함수를 이용해 구체적인 로직을 숨기고, 보다 선언적으로 변경할 수 있다.
이때 dispatch로 넘기게 되는 object를 action이라고 부른다. (action에는 최소한의 정보만 담아야 한다.)
action에 어떤 값을 담든 상관없지만 일반적으로 type을 넣는게 컨벤션이다.
type은 어떤 동작을 해야 하는지 명시하기 위한 것이다.

기본 형태

dispatch({
  // specific to component
  type: 'what_happened',
  // other fields go here
});

setState를 사용한 경우

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

dispatch를 사용한 경우

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

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

function handleDeleteTask(taskId) {
  dispatch(
  // action object
  {
    type: 'deleted',
    id: taskId,
  }
  );
}

2. reducer 함수 작성하기

reducer 함수는 state를 다루는 로직을 담은 함수이다.
argument로 current stateaction object를 받는다.

function yourReducer(state, action) {
  // return next state for React to set
}

reducer 함수에서는 state를 argument로 선언하기 때문에 컴포넌트 외부에서 state를 선언할 수 있다. 이러면 indentation level도 낮아지고 가독성을 높일 수 있다.
(추가로 if/else 문으로 써도 되지만 아래와 같이 switch 문으로 작성하는게 컨벤션이다. 가독성 차원에서 더 좋기 때문.)

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

참조

reducer 함수는 javascript의 reduce 메서드의 동작 방식과 동일한 아이디어를 사용한다.


3. reducer 함수 사용하기

1) 만든 reducer 함수를 import하기

2) useState 제거하기

3) userReducer 사용하기


전체 수정 코드

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

export default function TaskApp() {
  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,
    });
  }

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

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

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},
];
profile
모든게 새롭고 재밌는 프론트엔드 새싹

0개의 댓글