Kodokon kodokon.com

Подъём состояния и обмен данными

Размещай каждое состояние в ближайшем общем предке, чтобы делиться данными между несколькими компонентами, не дублируя их.

7 мин · 3 вопросов

Открыть этот урок в Kodokon

Два соседних компонента должны показывать одни и те же данные: например, поле поиска и список, который оно фильтрует. Локальное состояние в каждом из них не сработает, потому что они не видят друг друга. Решение - поднять состояние в их ближайшего общего предка: родитель владеет данными и передаёт их обратно вниз через пропсы.

JSX
import { useState } from "react";

function ProductPage({ products }) {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBar query={query} onChange={setQuery} />
      <ProductList products={products} query={query} />
    </>
  );
}
Родитель владеет query и делится им с обоими дочерними компонентами.

Дочерние компоненты становятся тогда контролируемыми: они получают значение только для чтения, а чтобы его изменить, вызывают функцию обратного вызова, переданную родителем. Данные текут вниз через пропсы, запросы на изменение поднимаются вверх через функции. Этот односторонний поток делает поведение приложения лёгким для понимания.

JSX
function SearchBar({ query, onChange }) {
  return (
    <input
      value={query}
      onChange={(e) => onChange(e.target.value)}
      placeholder="Search for a product"
    />
  );
}

function ProductList({ products, query }) {
  const visible = products.filter((p) =>
    p.name.toLowerCase().includes(query.toLowerCase())
  );
  return (
    <ul>
      {visible.map((p) => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}
SearchBar запрашивает изменение, ProductList его отражает.

У каждых данных должен быть единственный источник истины. Если два компонента хранят каждый свою копию одного и того же состояния, копии рано или поздно разойдутся: одна обновится, другая нет, и интерфейс станет несогласованным. Подъём состояния убирает этот риск. Когда данным нужно пройти через много уровней, композиция (следующий урок), а затем контекст облегчают передачу пропсов.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Где нужно разместить состояние, которое требуется двум соседним компонентам?
    • В первом из двух соседей, который передаст его второму
    • В их ближайшем общем предке
    • В глобальной переменной, объявленной вне React
    • В каждом из двух соседей, синхронизируя их вручную
  2. Как дочерний компонент может изменить состояние, которым владеет родитель?
    • Изменив полученный проп напрямую
    • Вызвав функцию обратного вызова, переданную родителем как проп
    • Используя useState с тем же начальным значением, что и у родителя
  3. Почему стоит избегать хранения одних и тех же данных в двух разных состояниях?
    • Это занимает слишком много памяти
    • React запрещает два вызова useState с одинаковым начальным значением
    • Две копии рано или поздно рассинхронизируются, и интерфейс станет несогласованным