App.jsx에 useState를 가져와서 작업을 상태에 저장하기 위해 App.jsx파일 맨 상단에 import 추가
props.tasks를 useState() 후크에 전달하기 위해 App()함수 내 맨 위에 const ... 선언
props.tasks 대신 task를 매핑한 결과가 되도록 taskList 매핑을 변경 taskList 상수 수정
// #스텝1: useState를 App.jsx에 가져와 상태로 저장하기 위해
import { useState } from "react";
// #스텝2: props.tasks를 useState() hook에 전달하기 위해 App()함수 맨 위에 const... 추가
const [tasks, setTasks] = useState(props.tasks);
// #스텝3: props.tasks 대신 tasks매핑 결과가 되도록 taskList상수 선언 수정
const taskList = tasks?.map((task) => (
<Todo
id={task.id}
name={task.name}
completed={task.completed}
key={task.id}
/>
));
문제점과 수정
addTask() 함수에서 작업 목록 업데이트에 사용하는 setTasks hook이 있는데, 현재 여기서 addTask()의 name 인수를 setTasks로 전달할 수 없는 문제... "tasks는 객체의 배열"이고 "name은 문자열"이기 때문.
addTask() 함수가 각 작업에 똑같은 ID를 부여하는 문제 → nanoid 라이브러리 사용
작업 수에 상관없이 제목에 갯수 변경 안 됨 → App()에 taskList 카운팅 및 텍스트 변경처리
// #문제해결1: addTask()함수 내 newTask객체를 만들고 배열에 추가
function addTask(name) {
const newTask = { id: "id", name, completed: false };
setTasks([...tasks, newTask]);
}
// #문제해결2-1: 고유 ID를 주기 위해 터미널에서 nanoid 라이브러리 설치
// npm install nanoid 또는 yarn add nanoid
// #문제해결2-2: App.jsx 상단에 import 추가
import { nanoid } from "nanoid";
// #문제해결2-3: addTask()를 업데이트해서 각 작업 ID가 "todo-" + nanoid라이브러리의 문자열이 되도록
const newTask = { id: `todo-${nanoid()}`, name, completed: false };
// #문제해결3-1: App()정의 내부에 추가
const headingText = `${taskList.length} tasks remaining`;
// #문제해결3-2: tasks를 따로 빼기 위해 tasksNoun 생성
const tasksNoun = taskList.length !== 1 ? "tasks" : "task";
const headingText = `${taskList.length} ${tasksNoun} remaining`;
// #문제해결3-3: <h2> 텍스트를 headingText로 수정
<h2 id="list-heading">{headingText}</h2>
테스팅
App() 컴포넌트에 taskList상수 선언 바로 윗부분에 toggleTaskCompleted()함수 생성
taskList 안의 <Todo />컴포넌트의 props에 toggleTaskCompleted 추가
Todo.jsx로 가서 <input />요소에 onChange 이벤트 추가.
App.jsx에서 toggleTaskCompleted() 함수 수정. → tasks 배열에 매핑되는 updatedTasks 상수를 정의, 작업의 id props가 함수의 id값과 일치하면 새 객체를 만들고 반환 전 해당 객체의 completed props를 토글. 일치하지 않으면 원래의 객체를 반환 → 이후 다음 상태를 업데이트하기 위해 새로운 배열로 setTasks()를 호출함
// #스텝1: taskList 상수 선언 바로 위에 toggleTaskCompleted()함수 생성
function toggleTaskCompleted(id) {
console.log(tasks[0]);
}
// #스텝2: taskList 내부에 <Todo />컴포넌트의 props에 toggleTaskCompleted 추가
const taskList = tasks.map((task) => (
<Todo
id={task.id}
name={task.name}
completed={task.completed}
key={task.id}
toggleTaskCompleted={toggleTaskCompleted}
/>
));
// #스텝3: 익명 함수를 사용해서 props.id 매개변수로 props.toggleTaskCompleted() 호출
<input
id={props.id}
type="checkbox"
defaultChecked={props.completed}
onChange={() => props.toggleTaskCompleted(props.id)}
/>
// #스텝4: 토글된 작업의 completed props만 변경하기~ App.jsx의 toggleTaskCompleted() 함수 수정
function toggleTaskCompleted(id) {
const updatedTasks = tasks.map((task) => {
// if this task has the same ID as the edited task
if (id === task.id) {
// use object spread to make a new object
// whose `completed` props has been inverted
return { ...task, completed: !task.completed };
}
return task;
});
setTasks(updatedTasks);
}
작업 삭제 기능 만들기
toggleTaskCompleted() 아래쪽에 deleteTask() 추가
<Todo />컴포넌트 배열에 다른 콜백 props를 추가
Todo.jsx 내부에 삭제 버튼 수정
deleteTask() 호출이 잘 되는지 확인 후 deleteTask()에서 setTasks() hook 호출을 위해 App.jsx에서 deleteTask() 수정
// #스텝1: App.jsx에서 toggleTaskCompleted() 아래쪽에 deleteTask() 함수 선언
function deleteTask(id) {
console.log(id);
}
// #스텝2: <Todo /> 컴포넌트 배열에 콜백 props 추가
const taskList = tasks.map((task) => (
<Todo
id={task.id}
name={task.name}
completed={task.completed}
key={task.id}
toggleTaskCompleted={toggleTaskCompleted}
deleteTask={deleteTask}
/>
));
// #스텝3: Todo.jsx의 삭제버튼 수정
<button
type="button"
className="btn btn__danger"
onClick={() => props.deleteTask(props.id)}>
Delete <span className="visually-hidden">{props.name}</span>
</button>
// #스텝4: App.jsx의 deleteTask()함수 수정
function deleteTask(id) {
const remainingTasks = tasks.filter((task) => id !== task.id);
setTasks(remainingTasks);
}
댓글