Read 기능을 이용하면 배열에 저장한 여러 할 일 아이템을 반복해서 페이지에 렌더링 할 수 있다.
(...)
function App() {
(...)
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} />
</div>
);
}
export default App;
import TodoItem from "./TodoItem";
import "./TodoList.css";
const TodoList = ({ todo }) => { // Props를 구조 분해 할당한다.
return (
<div className="TodoList">
<h4>Todo List 🌱</h4>
<input className="searchbar" placeholder="검색어를 입력하세요" />
<div className="list_wrapper">
{todo.map((it) => ( // 배열 todo의 모든 요소를 순차적으로 순회하여 HTML로 변환한다.
<div>{it.content}</div>
))}
</div>
</div>
);
};
export default TodoList;
import TodoItem from "./TodoItem";
import "./TodoList.css";
const TodoList = ({ todo }) => {
return (
<div className="TodoList">
<h4>Todo List 🌱</h4>
<input className="searchbar" placeholder="검색어를 입력하세요" />
<div className="list_wrapper">
{todo.map((it) => (
<TodoItem {...it} /> // TodoItem 컴포넌트에 it의 프로퍼티를 Props로 전달한다.
))}
</div>
</div>
);
};
export default TodoList;
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate }) => { // Props를 구조 분해 할당한다.
return (
<div className="TodoItem">
<div className="checkbox_col">
<input checked={isDone} type="checkbox" /> // 체크 여부를 isDone으로 설정한다.
</div>
<div className="title_col">{content}</div> // 할 일을 페이지에 표시하기 위해 렌더링한다.
<div className="date_col">
{new Date(createdDate).toLocaleDateString()} // 문자열로 변환해 렌더링한다.
</div>
<div className="btn_col">
<button>삭제</button>
</div>
</div>
);
};
export default TodoItem;
import TodoItem from "./TodoItem";
import "./TodoList.css";
const TodoList = ({ todo }) => {
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{todo.map((it) => (
<TodoItem key={it.id} {...it} /> // 컴포넌트에 key로 할 일 아이템의 id를 전달한다.
))}
</div>
</div>
);
};
export default TodoList;
TodoList 컴포넌트에서 특정 할 일을 검색하는 기능을 만들어보자.
import { useState } from "react"; // react 라이브러리에서 useState 리액트 훅을 불러온다.
(...)
const TodoList = ({ todo }) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => { // 검색 폼의 onChange 이벤트 핸들러 onChangeSearch를 만든다.
setSearch(e.target.value);
};
return (
<div className="TodoList">
<h4>Todo List 🌱</h4>
<input
value={search} // 검색 폼의 value로 State 변수 search를 설정한다.
onChange={onChangeSearch} // 검색 폼의 onChange 이벤트 핸들러를 onChangeSearch로 설정한다.
className="searchbar"
placeholder="검색어를 입력하세요"
/>
<div className="list_wrapper">
{todo.map((it) => (
<TodoItem key={it.id} {...it} />
))}
</div>
</div>
);
};
export default TodoList;
(...)
const TodoList = ({ todo }) => {
(...)
const getSearchResult = () => {
return search === ""
? todo
: todo.filter((it) => it.content.includes(search));
// todo 배열에서 search의 내용과 일치하는 아이템만 필터링해 반환한다.
};
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{getSearchResult().map((it) => (
// 함수 getSearchResult의 결과값을 map 메서드를 이용해 리스트로 렌더링한다.
<TodoItem key={it.id} {...it} />
))}
</div>
</div>
);
};
export default TodoList;
(...)
const getSearchResult = () => {
return search === ""
? todo
: todo.filter((it) =>
it.content.toLowerCase().includes(search.toLowerCase())
);
};
(...)
(...)
function App() {
(...)
const onUpdate = (targetId) => { // TodoItem 체크박스에 틱이 발생했을 때 호출하는 함수이다.
setTodo(
todo.map((it) => {
if (it.id === targetId) { // 배열 todo 에서 id가 targetId와 일치하는 요소를 찾으면,
return { // isDone 프로퍼티 값을 토글한 새 배열을 만들어 인수로 전달한다.
...it,
isDone: !it.isDone,
};
} else {
return it;
}
})
);
};
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} onUpdate={onUpdate} /> // Props로 함수 onUpdate를 전달한다.
</div>
);
}
export default App;
(...)
const onUpdate = (targetId) => {
setTodo(
todo.map((it) =>
it.id === targetId ? { ...it, isDone: !it.isDone } : it
)
);
};
(...)
(...)
const TodoList = ({ todo, onUpdate }) => { // Props를 구조 분해 할당한다.
(...)
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{getSearchResult().map((it) => (
<TodoItem key={it.id} {...it} onUpdate={onUpdate} /> // 함수 onUpdate를 Props로 전달한다.
))}
</div>
</div>
);
};
export default TodoList;
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate, onUpdate }) => { // Props를 구조 분해 할당한다.
const onChangeCheckbox = () => { // onUpdate를 호출하고 인수로 현재 틱이 발생한 할 일 아이템의 id를 전달한다.
onUpdate(id);
};
return (
<div className="TodoItem">
<div className="checkbox_col">
<input onChange={onChangeCheckbox} checked={isDone} type="checkbox" />
</div>
<div className="title_col">{content}</div>
<div className="date_col">
{new Date(createdDate).toLocaleDateString()}
</div>
<div className="btn_col">
<button>삭제</button>
</div>
</div>
);
};
export default TodoItem;
(...)
function App() {
(...)
const onDelete = (targetId) => { // 매개변수 targetId에 삭제 할 일기 아이템의 id를 저장하고,
// 해당 id 요소를 뺀 새 배열로 todo를 업데이트 함으로써 대상 아이템을 삭제한다.
setTodo(todo.filter((it) => it.id !== targetId));
};
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} />
</div>
);
}
export default App;
(...)
const TodoList = ({ todo, onUpdate, onDelete }) => {
(...)
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{getSearchResult().map((it) => (
<TodoItem
key={it.id}
{...it}
onUpdate={onUpdate}
onDelete={onDelete} // 함수 onDelete를 리스트의 모든 TodoItem에 Props로 전달한다.
/>
))}
</div>
</div>
);
};
export default TodoList;
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate, onUpdate, onDelete }) => {
const onChangeCheckbox = () => {
onUpdate(id);
};
const onClickDelete = () => { // 함수 onDelete를 호출하고 인수로 해당 아이템의 id를 전달한다.
onDelete(id);
};
return (
<div className="TodoItem">
<div className="checkbox_col">
<input onChange={onChangeCheckbox} checked={isDone} type="checkbox" />
</div>
<div className="title_col">{content}</div>
<div className="date_col">
{new Date(createdDate).toLocaleDateString()}
</div>
<div className="btn_col">
<button onClick={onClickDelete}>삭제</button>
</div>
</div>
);
};
export default TodoItem;
const onUpdate = (targetId) => {
setTodo(
todo.map((it) => {
if (it.id === targetId) {
return {
...it,
isDone: !it.isDone,
};
} else {
return it;
}
})
);
2. 함수 onDelete를 호출하고 인수로 해당 아이템의 id를 전달하는 onClickDelete 함수를 구현해 봐라.
const onUpdate = (targetId) => {
setTodo(
todo.map((it) =>
it.id === targetId ? { ...it, isDone: !it.isDone } : it
)
);
};
const onClickDelete = () => {
onDelete(id);
};
이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).
Editor: 숨숨
[리액터 스타터2] 9장. 컴포넌트 트리에 데이터 공급하기 (0) | 2024.01.05 |
---|---|
[리액터 스타터2] 7장. useReducer와 상태 관리 / 8장. 최적화 (2) | 2023.12.29 |
[리액터 스타터2] project 2 [할 일 관리] 앱 만들기 1 (0) | 2023.12.01 |
[리액터 스타터2] Hooks (1) | 2023.11.24 |
[리액트 스타터2] project 1 [카운터] 앱 만들기 ~ 6장. 라이프 사이클과 리액트 개발자 도구 (0) | 2023.11.17 |