App 컴포넌트의 State 변수 todo에는 배열 형태로 여러 개의 할 일 아이템이 저장되어 있습니다. 배열 todo를 TodoList 컴포넌트에 Props로 전달합니다.
(...)
function App() {
(...)
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} />
</div>
);
}
export default App;
TodoList 컴포넌트에서는 App에서 Props로 전달된 todo를 리스트로 렌더링해야 합니다. 리액트에서 배열 데이터를 렌더링할 때는 배열 메서드 map을 주로 이용합니다. map을 이용하면 HTML 또는 컴포넌트를 순회하면서 매 요소를 반복하여 렌더링합니다.
TodoList 컴포넌트에서 배열 메서드 map을 이용해 HTML 요소를 반복해 렌더링합니다. 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) => ( ②
<div>{it.content}</div>
))}
</div>
</div>
);
};
export default TodoList;
페이지를 새로고침(<F5>)하고 렌더링 결과를 확인하면, todo에 저장된 3개의 할 일을 HTML로 반복해 페이지에 렌더링합니다.
배열을 이용해 컴포넌트를 반복해 렌더링합니다.
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} /> ①
))}
</div>
</div>
);
};
export default TodoList;
TodoItem 컴포넌트에 전달된 Props를 이 컴포넌트에서 사용할 수 있도록 다음과 같이 수정합니다.
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate }) => { ①
return (
<div className="TodoItem">
<div className="checkbox_col">
<input 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;
저장한 다음, 결과를 확인할 수 있도록 할 일 입력 폼에 ‘독서하기’라는 새 아이템을 추가합니다.
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} /> ①
))}
</div>
</div>
);
};
export default TodoList;
먼저 사용자가 입력하는 검색어를 처리할 State 변수를 만든 다음, 검색 폼에서 사용자가 입력한 내용을 처리하는 기능을 만듭니다. TodoList.js를 다음과 같이 수정합니다.
import { useState } from "react"; // ①
(...)
const TodoList = ({ todo }) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => { // ②
setSearch(e.target.value);
};
return (
<div className="TodoList">
<h4>Todo List 🌱</h4>
<input
value={search} // ③
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));
};
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{getSearchResult().map((it) => ( ②
<TodoItem key={it.id} {...it} />
))}
</div>
</div>
);
};
export default TodoList;
이제 검색 기능이 잘 구현되었습니다. 페이지에서 테스트 해 볼 수 있습니다.
(...)
function App() {
(...)
const onUpdate = (targetId) => { ①
setTodo(
todo.map((it) => { ②
if (it.id === targetId) {
return {
...it,
isDone: !it.isDone,
};
} else {
return it;
}
})
);
};
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} onUpdate={onUpdate} /> ③
</div>
);
}
export default App;
이제 TodoList에서 TodoItem 컴포넌트에 함수 onUpdate를 전달해야 합니다. TodoList 컴포넌트를 다음과 같이 수정합니다.
(...)
const TodoList = ({ todo, onUpdate }) => { ①
(...)
return (
<div className="TodoList">
(...)
<div className="list_wrapper">
{getSearchResult().map((it) => (
<TodoItem key={it.id} {...it} onUpdate={onUpdate} /> ②
))}
</div>
</div>
);
};
export default TodoList;
TodoItem을 다음과 같이 수정합니다.
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate, onUpdate }) => { ①
const onChangeCheckbox = () => { ②
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;
결과를 확인합니다. ‘React 공부하기’의 체크박스를 틱 했을 때 완료 여부를 표시하는 체크 표시가 나타나는지 확인합니다. 그리고 [Components]탭을 열고 TodoItem 컴포넌트에서 이 아이템의 isDone 프로퍼티가 true로 변경되는지도 확인합니다.
할 일 아이템의 삭제는 수정 기능과 유사한 흐름으로 진행됩니다. 사용자가 TodoItem의 <삭제> 버튼을 클릭하면 해당 할 일 아이템을 찾아 삭제하면 됩니다.
(...)
function App() {
(...)
const onDelete = (targetId) => { ①
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} ②
/>
))}
</div>
</div>
);
};
export default TodoList;
import "./TodoItem.css";
const TodoItem = ({ id, content, isDone, createdDate, onUpdate, onDelete }) => { ①
const onChangeCheckbox = () => {
onUpdate(id);
};
const onClickDelete = () => { ②
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;
1. prj 2의 Todo List.js 에서 검색 할 때 대소문자를 구별 하지 않도록 업그레이드 해보기
정답 :
(...)
const getSearchResult = () => {
return search === ""
? todo
: todo.filter((it) =>
it.content.toLowerCase().includes(search.toLowerCase()) ①
);
};
(...)
2. 삼항연산자를 이용해 간단하게 수정해보기
function App() {
(...)
const onUpdate = (targetId) => { ①
setTodo(
todo.map((it) => { ②
if (it.id === targetId) {
return {
...it,
isDone: !it.isDone,
};
} else {
return it;
}
})
);
};
return (
<div className="App">
<Header />
<TodoEditor onCreate={onCreate} />
<TodoList todo={todo} onUpdate={onUpdate} /> ③
</div>
);
}
export default App;
정답 :
(...)
const onUpdate = (targetId) => {
setTodo(
todo.map((it) =>
it.id === targetId ? { ...it, isDone: !it.isDone } : it
)
);
};
(...)
출처 : 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023)
Corner React.js 1
Editor: dalpaeng4
[React.js 1] 9장. 컴포넌트 트리에 데이터 공급하기 (1) | 2024.01.05 |
---|---|
[React.js 1] 7-8장. useReducer와 상태관리 & 최적화 (0) | 2023.12.29 |
[React.js 1] p2. 할 일 관리 앱 만들기 (0) | 2023.12.01 |
[React.js 1] 8장. Hooks (0) | 2023.11.24 |
[React.js 1] p1. 카운터 앱 만들기, 6장. 라이프 사이클과 리액트 개발자 도구 (0) | 2023.11.17 |