[react] Todo list 만들어 보기

0
post-thumbnail

App.js

import {useCallback, useRef, useState} from "react";
import style from './css/App.module.css';
import TodoTemplate from "./components/TodoTemplate";
import TodoInsert from "./components/TodoInsert";
import TodoList from "./components/TodoList";

const App = () => {
    const [todos, setTodos] = useState([]);

    const nextId = useRef(0);

    const onInsert = useCallback((text) => {
        const todo = {
            id:nextId.current,
            text,
            checked: false,
        };
        setTodos(todos.concat(todo));
        nextId.current += 1;
    },[todos]);

    const onRemove = useCallback((id) => {
        setTodos(todos.filter((todo) => todo.id !== id));
    },[todos])

    const onToggle = useCallback((id) => {
        setTodos(todos.map((todo) => (todo.id === id ? { ...todo, checked: !todo.checked } : todo)));
    })

    return (
        <TodoTemplate>
            <TodoInsert onInsert={onInsert}/>
            <TodoList todos={todos} onRemove={onRemove} onToggle={onToggle}/>
        </TodoTemplate>
    )
}

export default App;

TodoTemplate.js

import style from "../css/TodoTemplate.module.css";

const TodoTemplate = ({children}) => {
    return (
        <div className={style.TodoTemplate}>
            <div className={style.appTitle}>일정 관리</div>
            <div className={style.content}>{children}</div>
        </div>
    );
}

export default TodoTemplate;

TodoInsert.js

import {MdAdd} from "react-icons/md";
import style from "../css/TodoInsert.module.css";
import { useState, useCallback, useRef, useEffect } from "react";

const TodoInsert = ({ onInsert }) => {
    const [value, setValue] = useState("");

    const onInputChange = useCallback((e) => {
        setValue(e.target.value);
    }, []);

    const onInputSubmit = useCallback((e) => {
        // App,js에 text를 넘겨줌
        onInsert(value);
        // 그리곤 value값 초기화
        setValue("");
        e.preventDefault();
    }, [onInsert, value]);

    const todoInputFocus = useRef();
    useEffect(() => {
        todoInputFocus.current.focus();
    }, []);

    return (
        <>
        <form className={style.TodoInsert} onSubmit={onInputSubmit}>
            <input placeholder="할 일을 입력하세요" ref={todoInputFocus} value={value} onChange={onInputChange} />
            <button type="submit">
                <MdAdd/>
            </button>
        </form>
        </>
    )
}

export default TodoInsert;

TodoList.js

import TodoItem from "./TodoItem";
import style from "../css/TodoList.module.css";

const TodoList = ({todos, onToggle, onRemove}) => {
    return (
        <div className={style.TodoList}>
            {todos.map((todo) => (
                <TodoItem todo={todo} key={todo.id} onRemove={onRemove} onToggle={onToggle}/>
            ))}
        </div>
    )
}

export default TodoList;

TodoItem.js

import { MdCheckBoxOutlineBlank, MdCheckBox, MdRemoveCircleOutline, MdOutlineModeEdit } from "react-icons/md";
import cn from "classnames";
import style from "../css/TodoItem.module.css";

const TodoItem = ({todo, onRemove, onToggle}) => {
    const { id, text, checked } = todo;

    return (
        <div className={style.TodoListItem}>
          <div className={cn(style.checkbox, { checked })} onClick={() => onToggle(id)}>
            {checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
            <div className={style.text}>{text}</div>
          </div>
          <div className={style.edit}><MdOutlineModeEdit /></div>
          <div className={style.remove} onClick={() => onRemove(id)}><MdRemoveCircleOutline /></div>
        </div>
      );
}

export default TodoItem;

우선 수정하기 기능 제외하고 할일 작성/삭제 구현 완료

App.moduel.css

.App {
    text-align: center;
  }
  
  .App-logo {
    height: 40vmin;
    pointer-events: none;
  }
  
  .App-header {
    background-color: #282c34;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: calc(10px + 2vmin);
    color: white;
  }
  
  .App-link {
    color: #61dafb;
  }

TodoInsert.moduel.css


.TodoInsert {
    display: flex;
    background: #495057;
  }


  input {
    background: none;
    outline: none;
    border: none;
    padding: 0.5rem;
    font-size: 1.125rem;
    line-height: 1.5;
    color: white;
    flex: 1;
  }
  input::placeholder {
    color: #dee2e6;
  }
  button {
    background: none;
    outline: none;
    border: none;
    background: #868e96;
    color: white;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.5rem;
    display: flex;
    align-items: center;
    cursor: pointer;
    transition: 0.1s background ease-in;
  }
  button:hover {
    background: #adb5bd;
  }

TodoTemplate.module.css

.TodoTemplate {
    width: 512px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 6rem;
    border-radius: 4px;
    overflow: hidden;
  }
  
  .TodoTemplate .appTitle {
    background: #22b8cf;
    color: white;
    height: 4rem;
    font-size: 1.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .TodoTemplate .content {
    background: white;
  }

TodoList.module.css

.TodoList {
    min-height: 320px;
    max-height: 513px;
    overflow-y: auto;
  }

TodoListItem.module.css

.TodoListItem {
    padding: 1rem;
    display: flex;
    align-items: center;
  }



  .TodoListItem:nth-child(even) {
    background: #f8f9fa;
  }
  .TodoListItem .checkbox {
    cursor: pointer;
    flex: 1;
    display: flex;
    align-items: center;
  }
  .TodoListItem svg {
    font-size: 1.5rem;
  }
  .TodoListItem .text {
    margin-left: 0.5rem;
    flex: 1;
  }
  .TodoListItem.checked svg {
    color: #22b8cf;
    
  }
  .TodoListItem.checked .text {
    color: #adb5bd;
    text-decoration: line-through;
  }

  .remove {
    display: flex;
    align-items: center;
    font-size: 1.5rem;
    color: #ff6b6b;
    cursor: pointer;
    transition: .2s;
  }
  .remove:hover {
    color: #000;
  }
  .edit {
    display: flex;
    align-items: center;
    font-size: 1.5rem;
    color: #7c6bff;
    cursor: pointer;
    margin-right: 12px;
    transition: .2s;
  }
  .edit:hover {
    color: #000;
  }

  .TodoListItem ~ .TodoListItem {
    border-top: 1px solid #dee2e6;
  }
profile
일단 해. 그리고 잘 되면 잘 된 거, 잘 못되면 그냥 해본 거!

0개의 댓글