Stack/TypeScript
[TS / React] Redux로 To-do List 구현
7ingout
2022. 7. 29. 15:15
2022.07.28 - [Coding/TypeScript] - [TS / React] Redux로 Counter 구현
[TS / React] Redux로 Counter 구현
react-redux 1. 모듈리듀서 1) 액션타입 지정 2) 액션생성 함수 3) 초기값 4) 리듀서 함수 작성 -> 루트리듀서 2. 스토어 생성 const store = createStore(루트리듀서) 3.컴포넌트 생성 1) 컨테이너 컴포넌트 2)..
7ingout.tistory.com
초기 셋팅은 위 게시글 참고 ~
TS-REDUX-TUTORIAL
modules/todos.ts
// 액션타입 선언, 액션 생성 함수, 초기값, 리듀서
// 할 일 추가, 할 일 제거, 할 일 체크
// 액션타입 선언
const ADD_TODO = 'todos/ADD_TODO' as const;
const TOGGLE_TODO = 'todos/TOGGLE_TODO' as const;
const REMOVE_TODO = 'todos/REMOVE_TODO' as const;
let nextId = 1;
// 액션 생성 함수
export const addTodo = (text: string) => ({
type: ADD_TODO,
payload: {
id: nextId++,
text
}
})
export const toggleTodo = (id:number) => ({
type: TOGGLE_TODO,
payload: id
})
export const removeTodo = (id:number) => ({
type: REMOVE_TODO,
payload: id
})
// 액션 객체들에 대한 타입 작성
type TodosAction =
| ReturnType<typeof addTodo>
| ReturnType<typeof toggleTodo>
| ReturnType<typeof removeTodo>
// 상태에서 사용할 할일 항목 데이터 정리
export type Todo = {
id: number;
text: string;
done: boolean;
}
// 이 모듈에서 관리할 상태 타입 작성
export type TodoState = Todo[]
// 초기상태 선언
const initialState: TodoState = [];
// 리듀서 작성하기
function todos(
state: TodoState = initialState,
action: TodosAction
) : TodoState {
switch(action.type) {
case ADD_TODO:
return state.concat({
id:action.payload.id,
text: action.payload.text,
done: false
});
case TOGGLE_TODO:
return state.map(todo=>
todo.id === action.payload ? {...todo, done: !todo.done } : todo
)
case REMOVE_TODO:
return state.filter(todo => todo.id !== action.payload);
default:
return state
}
}
export default todos;
modules/index.ts
import { combineReducers } from "redux";
import counter from "./counter"
import todos from "./todos"
const rootReducer = combineReducers({ counter, todos });
export default rootReducer;
// 나중에 이 타입을 컨테이너 컴포넌트에서 불러와서 사용해야하므로
// 내보내줍니다.
export type RootState = ReturnType<typeof rootReducer>
// 프레젠테이션 컴포넌트
TodoInsert.tsx
import React, { FormEvent, useState } from 'react';
// props로 전달받을 데이터 타입을 지정
type TodoInsertProps = {
onInsert: (text:string) => void;
}
const TodoInsert = ({onInsert}: TodoInsertProps) => {
const [value, setValue] = useState('')
const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
}
const onSubmit = (e: FormEvent) => {
e.preventDefault();
onInsert(value);
setValue("")
}
return (
<div>
<form onSubmit={onSubmit}>
<input onChange={onChange} value={value} placeholder="할 일을 입력하세요"/>
<button type="submit">등록</button>
</form>
</div>
);
};
export default TodoInsert;
TodoItem.tsx
import React, { CSSProperties } from 'react';
import { Todo } from '../modules/todos';
// props 타입 지정
type TodoItemProps = {
todo: Todo;
onToggle: (id: number) => void;
onRemove: (id: number)=> void;
}
const TodoItem = ({ todo, onToggle, onRemove }: TodoItemProps) => {
const handleToggle = () => onToggle(todo.id)
const handleRemove = () => onRemove(todo.id)
const textStyle: CSSProperties= {
textDecoration: todo.done? 'line-through' : 'none'
}
const removeStyle: CSSProperties = {
color: 'red',
marginLeft: 8
}
return (
<li>
<span onClick={handleToggle} style={textStyle}>{todo.text}</span>
<span onClick={handleRemove} style={removeStyle}>X</span>
</li>
);
};
export default TodoItem;
TodoList.tsx
import React from 'react';
import { Todo } from '../modules/todos';
import TodoItem from './TodoItem';
// props 타입지정
type TodolistProps = {
todos: Todo[];
onToggle:(id:number) => void;
onRemove:(id:number) => void;
}
const TodoList = ({todos, onToggle, onRemove}: TodolistProps) => {
if(todos.length === 0 ) return <div>등록한 항목이 없습니다.</div>
return (
<ul>
{todos.map(todo=>(
<TodoItem todo={todo} onToggle={onToggle} onRemove={onRemove} key={todo.id}/>
))}
</ul>
);
};
export default TodoList;
//
containers/TodoApp.tsx
import React from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import TodoInsert from '../components/TodoInsert';
import TodoList from '../components/TodoList';
import { RootState } from '../modules';
import { addTodo, removeTodo, toggleTodo } from '../modules/todos';
const TodoApp = () => {
const todos = useSelector((state: RootState) => state.todos)
const dispatch = useDispatch();
const onInsert = (text: string) => {
dispatch(addTodo(text))
}
const onToggle = (id: number) => {
dispatch(toggleTodo(id))
}
const onRemove = (id: number) => {
dispatch(removeTodo(id))
}
return (
<div>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos} onToggle={onToggle} onRemove={onRemove}/>
</div>
);
};
export default TodoApp;
App.tsx
import React from 'react';
import './App.css';
// import ContainerCounter from './containers/ContainerCounter';
import TodoApp from './containers/TodoApp';
function App() {
return (
<div className="App">
{/* <ContainerCounter /> */}
<TodoApp />
</div>
);
}
export default App;