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;
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;
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;
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;
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 {
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 {
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 {
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 {
min-height: 320px;
max-height: 513px;
overflow-y: auto;
}
.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;
}