노력이 좋아서

<step91>'react(redux)_ts_todoList'

zoaseo 2022. 7. 29. 14:48

2022.07.28 - [공부가 좋아서] - 'react(redux)_ts_counter'

 

<step90>'react(redux)_ts_counter'

1) npm create-react-app ts-redux-tutorial --template typescript npm install redux npm install react-redux npm install @types/react-redux react-redux 1. 모듈 리듀서 1) 액션타입 지정 2) 액션생성 함수..

zoaseo.tistory.com

1) 

프레젠테이션 컴포넌트

TodoInsert.tsx

TodoList.tsx

TodoItem.tsx

 

2)

components/TodoInsert.tsx

import React, { FormEvent, useState } from 'react';

// 프롭스로 전달받을 데이터 타입을 지정
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;

components/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;

components/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 (
        <>
            <TodoInsert onInsert={onInsert}/>
            <TodoList todos={todos} onToggle={onToggle} onRemove={onRemove}/>  
        </>
    );
};

export default TodoApp;

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>

App.tsx

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;