[React - TS] TODO APP - useReducer 적용

빛트·2022년 5월 25일
1

REACT-TS-TODO

목록 보기
6/6
post-thumbnail

여는 말

TS를 익히기 위해 만들어본 TODO 앱입니다

이번 글에서는 useReducer를 사용해서 상태 업데이트 로직을 분리하는 것을

목표로 합니다

'REACT-TS-TODO' 시리즈의 마지막 글입니다


소스

1. '리스트 표현', '추가' 수정

TodoContext.tsx

먼저, Todo List를 표현하고 새로운 Todo를 추가할 수 있도록 합니다

// TodoContext.tsx
import { createContext, Dispatch } from "react";

interface ITodoContext {
  state: TodoState;
  dispatch: Dispatch<TodoAction>;
}

export type TodoState = {
  todos: ITodo[];
  newIdx: number;
};

type TodoAction = { type: "ADD_TODO"; payload: { todo: ITodo } };

export const todoReducer = (
  state: TodoState,
  action: TodoAction
): TodoState => {
  switch (action.type) {
    case "ADD_TODO":
      const newTodo = [...state.todos, action.payload.todo];
      return {
        ...state,
        todos: newTodo,
        newIdx: state.newIdx + 1,
      };

    default:
      return state;
  }
};

TodoList.tsx

// TodoList.tsx
//..
const initState: TodoState = {
  todos: [
    { idx: 100, title: "todo-100", project: "project-100" },
    { idx: 101, title: "todo-101", project: "project-101" },
  ],
  newIdx: 1,
  editIdx: 0,
};

const TodoList = () => {
  const [state, dispatch] = useReducer(todoReducer, initState);
  const { todos, editIdx } = state;

  const Items = () => {
    return todos.map((item) => {
      if (item.idx === editIdx) {
        return (
          <EditTodo {...item}></EditTodo>
        );
      } else {
        return (
          <Todo {...item}></Todo>
        );
      }
    });
  };

  return (
    <div className="todo-list-container">
      <TodoContext.Provider value={{ state, dispatch }}>
        {Items()}
        <CreateTodo />
      </TodoContext.Provider>
    </div>
  );
};

CreateTodo.tsx

// CreateTodo.tsx
const CreateTodo = () => {
  const { state, dispatch } = useContext(TodoContext);
  //...

  const handleClickCreateBtn = () => {
    let todo = { idx: state.newIdx, title: title, project: project };
    dispatch({ type: "ADD_TODO", payload: { todo } });
    setEmpty();
  };
  // ...
}

2. '수정', '삭제' 수정

수정, 삭제 기능도 reducer로 옮겨줍니다

TodoContext.tsx

// TodoContext.tsx
// ... 

export type TodoState = {
  todos: ITodo[];
  newIdx: number;
  editIdx: number;
};

type TodoAction =
  | { type: "EDIT_START"; payload: { idx: number } }
  | { type: "ADD_TODO"; payload: { todo: ITodo } }
  | { type: "EDIT_TODO"; payload: { todo: ITodo } }
  | { type: "DELETE_TODO"; payload: { idx: number } };

export const todoReducer = (
  state: TodoState,
  action: TodoAction
): TodoState => {
  let newTodos: ITodo[];
  switch (action.type) {
    case "EDIT_START":
      return {
        ...state,
        editIdx: action.payload.idx,
      };
    case "ADD_TODO":
      newTodos = [...state.todos, action.payload.todo];
      return {
        ...state,
        todos: newTodos,
        newIdx: state.newIdx + 1,
      };
    case "EDIT_TODO":
      newTodos = state.todos.map((item) => {
        if (item.idx === action.payload.todo.idx) {
          return action.payload.todo;
        } else {
          return item;
        }
      });
      return {
        ...state,
        todos: newTodos,
        editIdx: 0,
      };
    case "DELETE_TODO":
      newTodos = state.todos.filter((item) => {
        return item.idx !== action.payload.idx;
      });
      return {
        ...state,
        todos: newTodos,
      };
    default:
      return state;
  }
};

Todo.tsx

// Todo.tsx
const Todo = (props: ITodo) => {
  const { dispatch } = useContext(TodoContext);

  const handleClickDeleteBtn = () => {
    dispatch({ type: "DELETE_TODO", payload: { idx: props.idx } });
  };

  const handleClickUpdateBtn = () => {
    dispatch({ type: "EDIT_START", payload: { idx: props.idx } });
  };

//... 
}

EditTodo.tsx

// EditTodo.tsx
const EditTodo = (props: EditTodoProp) => {
  const { dispatch } = useContext(TodoContext);
  // ...

  const handleClickSubmitBtn = () => {
    const newTodo = { idx: props.idx, title, project };
    dispatch({ type: "EDIT_TODO", payload: { todo: newTodo } });
  };
  //...
}

닫는 말

전체 소스 : Github

profile
https://kangbit.github.io/posts

0개의 댓글