Todo를 클릭하면 삭제되게 해보자.
아이디로 검증하면 된다.
import classes from "./Todo.module.css";
const Todo: React.FC<{
text: string;
onRemoveTodo: () => void; //🔥props으로 내려받은 함수에 대해 타입 명시
}> = (props) => {
return (
<li
onClick={props.onRemoveTodo}
className={classes.item}
>
{props.text}
</li>
);
};
export default Todo;
여기에 왜 id 값은 없냐! 라고 묻는다면 코드 최대한 줄일 수 있는 방향으로 생각했을 때, <Todo />
컴포넌트의 상위 컴포넌트인 <Todos />
컴포넌트에서 onRemoveTodo()
함수에 bind()
메서드를 사용하여 id 값을 인자로 넣어 줄 수 있기 때문에 여기서 id 값을 또 props으로 내려 받거나 useRef()등으로 잡아올 필요가 없어진다.
그렇다면 <Todos />
컴포넌트를 한 번 보자.
import React from "react";
import Todo from "./Todo";
import classes from "./Todos.module.css";
import TodoClass from "../models/todo";
type Props = {
items: TodoClass[]; //items은 Todo 객체로 채워진 배열
children?: React.ReactNode; //옵셔널
onRemoveTodo: (id: string) => void; //🔥props으로 내려받은 함수에 대해 타입 명시
};
const Todos: React.FC<Props> = (props) => {
//console.log("✅", props.onRemoveTodo);
//console.log("✅✅", props.onRemoveTodo.bind(null));
return (
<ul className={classes.todos}>
{props.items.map((item) => (
<Todo
key={item.id}
text={item.text}
// id={item.id} //❗️bind 덕분에 굳이 id 하위 컴포넌트로 안 내려줘도 됨
onRemoveTodo={props.onRemoveTodo.bind(null, item.id)}
/>
//따라서 items의 각요소의 id, text 프로퍼티를 사용할 수 있다.
))}
</ul>
);
};
export default Todos;
onRemoveTodo()
를 통해 호출될 함수 안에서 무엇을 가르킬지 this
를 지정해 줄 수 있다.null
console.log("✅", props.onRemoveTodo);
console.log("✅✅", props.onRemoveTodo.bind(null));
onRemoveTodo()
가 매개변수로 받을 수 있게되므로 item.id
값 넣어주기bind()를 활용하여 함수에 인자를 넣어줄 수 있으므로, 굳이 하위 컴포넌트 까지 내려가지 않고서도 중간에 있는 컴포넌트에서 bind로 item.id값을 줄 수 있기 때문에 코드를 좀 덜 쓸 수 있다.
import { useState } from "react";
import "./App.css";
import NewTodo from "./components/NewTodo";
import Todos from "./components/Todos";
import TodoClass from "./models/todo";
function App() {
const [todos, setTodos] = useState<TodoClass[]>([]);
const addTodoHandler = (newTodoText: string) => {
const newTodo = new TodoClass(newTodoText);
setTodos((prevTodos) => {
return prevTodos.concat(newTodo);
});
};
// Todo 제거하는 핸들러
const removeTodoHandler = (clickedTodoId: string) => {
setTodos((prevTodos) => {
//배열의 아이템들 다 돌려서 id 값이 removeTodoHandler가 받은 인자인 clickedTodoId값과 같지 않은 것만 모아 새로운 배열로 반환하기
return prevTodos.filter((todo) => todo.id !== clickedTodoId);
});
};
return (
<div>
<NewTodo onAddTodo={addTodoHandler} />
<Todos items={todos} onRemoveTodo={removeTodoHandler} />
</div>
);
}
export default App;