todolist 만들기(3) useReducer, useContext 실전

jaehan·2022년 9월 9일
0


앞에서 useReduceruseContext사용법을 봤었는데 이번에는 두 hook을 한파일안에 같이 사용해볼 에정이다.

useReducer

TodoContext.js

import React, { createContext, useContext, useReducer, useRef } from "react";

const initialTodos = [
  {
    id: 1,
    text: "React 공부하기",
    done: true,
  },
  {
    id: 2,
    text: "velog 쓰기",
    done: false,
  },
  {
    id: 3,
    text: "학교 공부하기",
    done: false,
  },
];

function todoReducer(state, action) {
  switch (action.type) {
    case "CREATE":
      return state.concat(action.todo);
    case "TOGGLE":
      return state.map((todo) =>
        todo.id === action.id ? { ...todo, done: !todo.done } : todo
      );
    case "REMOVE":
      return state.filter((todo) => todo.id !== action.id);
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
}

todolist iteme들을 임의로 세개정도 만들어 주고

action type에 따라 switch문으로 reducer를 정의해 주었다.

useContext

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();
const TodoNextIdContext = createContext();

export function TodoProvider({ children }) {
  const [state, dispatch] = useReducer(todoReducer, initialTodos);
  const nextId = useRef(5);

  return (
    <TodoStateContext.Provider value={state}>
      <TodoDispatchContext.Provider value={dispatch}>
        <TodoNextIdContext.Provider value={nextId}>
          {children}
        </TodoNextIdContext.Provider>
      </TodoDispatchContext.Provider>
    </TodoStateContext.Provider>
  );
}

이후에 createContextcontext를 만들어 주고 context 의 value값으로 useReducer로 가져온 값들을 넣어주었다.

📌 불필요한 렌더링을 막기 위해 context들을 따로 만들어줬다.

export function useTodoState() {
  const context = useContext(TodoStateContext);
  if (!context) {
    throw new Error("Cannot find TodoProvider");
  }
  return context;
}
export function useTodoDispatch() {
  const context = useContext(TodoDispatchContext);
  if (!context) {
    throw new Error("Cannot find TodoProvider");
  }
  return context;
}

export function useTodoNextId() {
  const context = useContext(TodoNextIdContext);
  if (!context) {
    throw new Error("Cannot find TodoProvider");
  }
  return context;
}

따로 만든 context를 별도의 hook으로 따로 뺴서 컴포넌드 단에서 컨텍스트 별로 가져갈 수 있도록 해주었다.

적용

function TodoItem({ id, done, text }) {
  const dispatch = useTodoDispatch();
  const onToggle = () =>
    dispatch({
      type: "TOGGLE",
      id,
    });
  const onRemove = () =>
    dispatch({
      type: "REMOVE",
      id,
    });

  return (
    <TodoItemBlock>
      <CheckCircle onClick={onToggle} done={done}>
        {done && <MdDone />}
      </CheckCircle>
      <Text done={done}>{text}</Text>
      <Remove onClick={onRemove}>
        <MdDelete />
      </Remove>
    </TodoItemBlock>
  );
}

위 처럼 hook으로 dispatch를 가져올 수 있고 action.tyoe으로 함수를 사용 할 수 있다.

결과

전체코드

0개의 댓글