TodoItem.jsx input쪽에서 checked를 사용해서 발생하는 경고
를 없애기위해 onChange나 readOnly 속성을 줘야해서
임시로 readOnly 속성을 준다.
TodoItem.jsx
import "./TodoItem.css"
const TodoItem = ({id, isDone, content, date}) => {
return (
<div className="TodoItem">
<input readOnly checked={isDone} type="checkbox" />
<div className="content ">{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button>삭제</button>
</div>
);
};
export default TodoItem;
List.jsx에서 구분하는 key값이 없어서 발생하는 경고를 없애기위해
TodoItem에 key로 todo.id를 전달해준다.
import "./List.css";
import TodoItem from "./TodoItem";
const List = ({ todos }) => {
return (
<div className="List">
<h4>🎈Todo List...🎈</h4>
<input placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{todos.map((todo)=>{
return <TodoItem key={todo.id} {...todo}/>;
})}
</div>
</div>
);
};
export default List;
수정후 개발자도구로 List를 확인해보면 key가 생성된 걸 확인할 수 있다.

List에 검색기능 추가하기(검색어를 관리하는 State 생성하고
input에 value로 search주고 onChange이벤트에 onChangeSearch이벤트핸들러
등록하고 만들기, 필터링된 todo를 반환해주는 getFilteredData 만들기
List.jsx
import "./List.css";
import TodoItem from "./TodoItem";
import { useState } from "react";
const List = ({ todos, onUpdate, onDelete }) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => {
setSearch(e.target.value);
};
const getFilteredData = () => {
if (search === "") {
return todos; //search가 비어있으면 전체를 리턴
}
return todos.filter((todo) => {
todo.content.includes(search); //필터메소드를 사용해서
//search를 포함하는 content만 리턴하도록한다.
});
};
const filteredTodos = getFilteredData();
return (
<div className="List">
<h4>🎈Todo List...🎈</h4>
<input value={search}
onChange={onChangeSearch}placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{filteredTodos.map((todo)=>{
return <TodoItem key={todo.id} {...todo}/>;
})}
</div>
</div>
);
};
export default List;
List.jsx
getFilteredData에서 소문자,대문자 모두 검색할수있도록 content와 search
toLowerCase로 수정하기
const getFilteredData = () => {
if (search === "") {
return todos; //search가 비어있으면 전체를 리턴
}
return todos.filter((todo) => {
todo.content.toLowerCase().includes(search.toLowerCase());
//필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
});
};
수정기능 만들기
App.jsx
import './App.css';
import { useState, useRef } from 'react';
import Header from './components/Header';
import Editor from './components/Editor';
import List from './components/List';
const testData = [
{
id:0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id:1,
isDone: false,
content: "밥먹기",
date: new Date().getTime(),
},
{
id:2,
isDone: false,
content: "운동하기",
date: new Date().getTime(),
},
];
function App() {
const [todos, setTodos] = useState(testData);
const idRef = useRef(3);
const onCreate = (content) => {
const newTodo = {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
};
setTodos([newTodo,...todos]);
};
const onUpdate = (targetID) => {
setTodos(todos.map((todo)=>{
if(todo.id === targetID){
return {...todo, isDone: !todo.isDone};
}
return todo;
}));
};
return (
<div className="App">
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate}/>
</div>
);
}
export default App;
onUpdate를 App에서 List에 전달하고 List에서 TodoItem으로 전달한다.
List.jsx
import "./List.css";
import TodoItem from "./TodoItem";
import { useState } from "react";
const List = ({ todos, onUpdate }) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => {
setSearch(e.target.value);
};
const getFilteredData = () => {
if (search === "") {
return todos; //search가 비어있으면 전체를 리턴
}
return todos.filter((todo) => {
todo.content.toLowerCase().includes(search.toLowerCase());
//필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
});
};
const filteredTodos = getFilteredData();
return (
<div className="List">
<h4>🎈Todo List...🎈</h4>
<input value={search}
onChange={onChangeSearch}placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{filteredTodos.map((todo)=>{
return <TodoItem key={todo.id} {...todo} onUpdate=
{onUpdate}/>;
})}
</div>
</div>
);
};
export default List;
readOnly 삭제하고 onChange 이벤트 등록하기
TodoItem.jsx
import "./TodoItem.css"
const TodoItem = ({id, isDone, content, date, onUpdate }) => {
const onChangeCheckBox = () => {
onUpdate(id);
};
return (
<div className="TodoItem">
<input onChange={onChangeCheckBox} checked={isDone} type="checkbox" />
<div className="content">{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button>삭제</button>
</div>
);
};
export default TodoItem;
체크하면 isDone 값이 true로 바뀌는걸 확인할 수 있다.

삭제기능 추가하기
App.jsx
import './App.css';
import { useState, useRef } from 'react';
import Header from './components/Header';
import Editor from './components/Editor';
import List from './components/List';
const testData = [
{
id:0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id:1,
isDone: false,
content: "밥먹기",
date: new Date().getTime(),
},
{
id:2,
isDone: false,
content: "운동하기",
date: new Date().getTime(),
},
];
function App() {
const [todos, setTodos] = useState(testData);
const idRef = useRef(3);
const onCreate = (content) => {
const newTodo = {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
};
setTodos([newTodo,...todos]);
};
const onUpdate = (targetID) => {
setTodos(todos.map((todo)=>{
if(todo.id === targetID){
return {...todo, isDone: !todo.isDone};
}
return todo;
}));
};
const onDelete = (targetID) =>{
setTodos(todos.filter((todo)=> todo.id !==
targetID));
};
return (
<div className="App">
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate}
onDelete={onDelete}/>
</div>
);
}
export default App;
List.jsx
import "./List.css";
import TodoItem from "./TodoItem";
import { useState } from "react";
const List = ({ todos, onUpdate, onDelete }) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => {
setSearch(e.target.value);
};
const getFilteredData = () => {
if (search === "") {
return todos; //search가 비어있으면 전체를 리턴
}
return todos.filter((todo) => {
todo.content.toLowerCase().includes(search.toLowerCase());
//필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
});
};
const filteredTodos = getFilteredData();
return (
<div className="List">
<h4>🎈Todo List...🎈</h4>
<input value={search}
onChange={onChangeSearch}placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{filteredTodos.map((todo)=>{
return <TodoItem key={todo.id} {...todo} onUpdate=
{onUpdate} onDelete={onDelete}/>;
})}
</div>
</div>
);
};
export default List;
TodoItem.jsx
import "./TodoItem.css"
const TodoItem = ({id, isDone, content, date, onUpdate, onDelete }) => {
const onChangeCheckBox = () => {
onUpdate(id);
};
const onClickDeleteButton = () => {
onDelete(id);
}
return (
<div className="TodoItem">
<input onChange={onChangeCheckBox} checked={isDone} type="checkbox" />
<div className={`content ${isDone ? 'done' : ''}`}>{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button onClick={onClickDeleteButton}>삭제</button>
</div>
);
};
export default TodoItem;