TS를 익히기 위해 만들어본 TODO 앱입니다
이번 글에서는 useReducer를 사용해서 상태 업데이트 로직을 분리하는 것을
목표로 합니다
'REACT-TS-TODO' 시리즈의 마지막 글입니다
먼저, 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
//..
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
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();
};
// ...
}
수정, 삭제 기능도 reducer로 옮겨줍니다
// 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
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
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