Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Подъём состояния

Состояние живёт в одном компоненте, а изменить его часто нужно из другого. Кнопка «Удалить» находится в 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);
}

Если данные принадлежат родителю, читайте их из пропсов, а меняйте через переданную функцию.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.