Подъём состояния
Состояние живёт в одном компоненте, а изменить его часто нужно из другого. Кнопка «Удалить» находится в TodoItem, а массив задач — в App. Решение называют подъёмом состояния: состояние хранят в ближайшем общем родителе, а детям передают данные и функции для их изменения через пропсы.
Функция в пропсе
Родитель передаёт ребёнку обработчик так же, как любое другое значение. Измените src/components/TodoItem.jsx:
// src/components/TodoItem.jsx
export default function TodoItem({ todo, onDelete }) {
return (
<li>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Удалить</button>
</li>
);
}TodoItem не знает, как устроено удаление. Он только сообщает: «нажали удалить задачу с таким id». Пропсы-обработчики принято называть с приставки on.
Передача через список
TodoList получает функцию от App и пробрасывает её каждому пункту — src/components/TodoList.jsx:
// src/components/TodoList.jsx
import TodoItem from './TodoItem.jsx';
export default function TodoList({ todos, onDelete }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} onDelete={onDelete} />
))}
</ul>
);
}Состояние в родителе
В src/App.jsx лежат и массив, и функция, которая его меняет:
// src/App.jsx
import { useState } from 'react';
import TodoList from './components/TodoList.jsx';
export default function App() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Купить молоко', done: false },
{ id: 2, text: 'Позвонить Олегу', done: true },
]);
function handleDelete(id) {
setTodos(todos.filter((todo) => todo.id !== id));
}
return <TodoList todos={todos} onDelete={handleDelete} />;
}Нажмите «Удалить» у любой задачи — она исчезнет из списка. Данные идут вниз через пропсы, а события поднимаются вверх через вызовы функций.
Отметка о выполнении
Так же подключается флажок. В TodoItem:
// src/components/TodoItem.jsx
export default function TodoItem({ todo, onToggle, onDelete }) {
return (
<li>
<input type="checkbox" checked={todo.done} onChange={() => onToggle(todo.id)} />
{todo.text}
<button onClick={() => onDelete(todo.id)}>Удалить</button>
</li>
);
}В App добавьте handleToggle из урока про массивы в состоянии и передайте onToggle={handleToggle} через TodoList. Клик по флажку отметит задачу.
Где держать состояние
| Ситуация | Где состояние |
|---|---|
| Нужно только одному компоненту | В нём самом |
| Нужно двум соседям | В их общем родителе |
| Нужно почти всему приложению | В корне или в контексте |
Частая ошибка
Копия пропса в собственном состоянии ребёнка не обновляется, когда меняется родитель:
// Неправильно: done запомнит первое значение и разойдётся с App.
export default function TodoItem({ todo }) {
const [done, setDone] = useState(todo.done);
}Если данные принадлежат родителю, читайте их из пропсов, а меняйте через переданную функцию.