Урок 02

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

Когда несколько компонентов должны использовать одни и те же данные, состояние поднимают в их общего родителя. Это называется lifting state up.

Проблема

Два компонента хотят знать одно значение:

function TemperatureInput() {
    const [temp, setTemp] = useState(0);
    return <input value={temp} onChange={(e) => setTemp(+e.target.value)} />;
}

function TemperatureDisplay() {
    // как получить temp?
}

TemperatureDisplay не может прочитать состояние TemperatureInput — оно локальное.

Решение

Состояние поднимается в родителя. Родитель передаёт значение и функцию обновления через пропсы.

function Calculator() {
    const [temp, setTemp] = useState(0);

    return (
        <>
            <TemperatureInput value={temp} onChange={setTemp} />
            <TemperatureDisplay value={temp} />
        </>
    );
}

function TemperatureInput({ value, onChange }: {
    value: number;
    onChange: (v: number) => void;
}) {
    return <input value={value} onChange={(e) => onChange(+e.target.value)} />;
}

function TemperatureDisplay({ value }: { value: number }) {
    return <p>{value}°C</p>;
}

Односторонний поток

Данные текут вниз через пропсы, события — вверх через функции обратного вызова.

App (state)
 ├── Child A (props: value)
 └── Child B (props: value, onChange)

Пример: корзина

function Shop() {
    const [cart, setCart] = useState<CartItem[]>([]);

    function addToCart(item: CartItem) {
        setCart([...cart, item]);
    }

    function removeFromCart(id: number) {
        setCart(cart.filter((i) => i.id !== id));
    }

    return (
        <>
            <ProductList onAdd={addToCart} />
            <Cart items={cart} onRemove={removeFromCart} />
            <Total items={cart} />
        </>
    );
}

Один источник истины — cart в Shop. Все остальные компоненты получают данные через пропсы.

Проблема prop drilling

Если компонентов много и состояние нужно глубоко в дереве — пропсы приходится прокидывать через промежуточные компоненты, которые их не используют:

<App user={user}>
    <Layout user={user}>
        <Sidebar user={user}>
            <Profile user={user} />

Layout и Sidebar не используют user, но вынуждены его передавать. Это называется prop drilling.

Решение — контекст (следующий урок).

Когда поднимать

  • Данные нужны нескольким компонентам
  • Компоненты — соседи или на разных уровнях
  • Есть общий родитель, который может хранить состояние

Когда не поднимать

  • Данные нужны одному компоненту
  • Подъём усложняет код без необходимости
  • Состояние нужно очень глубоко — тогда контекст или стор

Итоги

  • Подъём состояния — в ближайшего общего родителя
  • Данные вниз, события вверх
  • Один источник истины
  • Prop drilling — проблема, которую решает контекст