</> Praktum

Главная / React / State

Урок 3 · React · 25 минут

State: данные, которые меняют интерфейс

State хранит данные, которые могут меняться из-за действий пользователя. Когда state обновляется, React сам заново отображает нужную часть интерфейса.

useState

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

count — текущее значение, setCount — функция обновления. Нельзя менять count напрямую: React не узнает, что нужно обновить экран.

Обновление от прошлого значения

setCount((currentCount) => currentCount + 1);

Этот вариант надёжнее, если новое значение зависит от предыдущего или обновления идут подряд.

Ошибки

  • Писать count = count + 1 вместо вызова setCount.
  • Вызывать setState во время рендера без условия.
  • Хранить в state то, что можно вычислить из props.

State принадлежит конкретному экземпляру

Две карточки с useState(0) не делят счётчик. У каждой свой. Если число должно быть общим, state поднимают в родителя и передают вниз. Это и есть ответ на «почему соседняя карточка не обновилась».

Первый аргумент useState — начальное значение только при появлении компонента. Позже менять его снаружи бесполезно. Новые данные в уже живой компонент приходят через props или через установку state в эффекте, если это внешняя загрузка.

function Counter() {
  const [count, setCount] = useState(0);

  function increase() {
    setCount((value) => value + 1);
  }

  return <button onClick={increase}>Пройдено: {count}</button>;
}

Форма с функцией нужна, когда новое значение зависит от предыдущего. Два быстрых клика тогда не потеряются. setCount(count + 1) в одном обработчике дважды может прочитать одно и то же старое число.

Не клади в state лишнее

Полное имя из фамилии и имени можно собрать во время рендера. Отфильтрованный список тоже. В state оставляют источник: текст поля, выбранный фильтр, массив уроков. Иначе приходится помнить, какое из двух значений обновлять, и они разъезжаются.

Объект и массив заменяют целиком. Изменение lesson.done = true не сообщает React, что пора перерисовать. Нужна копия: setLesson({ ...lesson, done: true }).

После обновления

  • Не жди, что сразу после setCount переменная count уже новая. Она обновится в следующем рендере.
  • Тяжёлое начальное значение передай функцией в useState, чтобы не считать его каждый рендер.
  • Сброс формы — это установка пустых значений, а не удаление компонента тайком.
  • Один useState на всё приложение быстро становится нечитаемым. Дели по смыслу.
Запомни: Если интерфейс мигает старым значением, почти всегда новое состояние считают из старого снимка, а не из функции обновления.

Практика

Сделай кнопку «Показать описание», которая меняет состояние isVisible и показывает или скрывает текст.

Решение
const [isVisible, setIsVisible] = useState(false);
<button onClick={() => setIsVisible(!isVisible)}>Показать</button>

Как обновлять состояние без ошибок

useState хранит значение между рендерами и запускает новый рендер после обновления. Когда следующее значение зависит от предыдущего, передавай функцию: setCount(count => count + 1). Так обновление не использует устаревшее замыкание при нескольких быстрых действиях.

Массивы и объекты в состоянии нельзя изменять напрямую. Создай новую структуру через spread, map или filter. Не сохраняй то, что можно вычислить из props и другого state: дублированные данные легко расходятся. Состояние поднимай к ближайшему общему родителю только тогда, когда оно действительно нужно нескольким компонентам.

Разберём рабочий пример

const [lessons, setLessons] = useState([]);

function addLesson(title) {
  setLessons(current => [
    ...current,
    { id: crypto.randomUUID(), title }
  ]);
}

Не копируй пример вслепую: измени данные, убери одну строку и посмотри, как меняется результат. Такой эксперимент быстрее показывает роль каждой части кода, чем простое чтение.

Проверка перед использованием

  • Зависимое обновление использует функцию.
  • Объекты и массивы не мутируются.
  • В state нет вычисляемых дубликатов.
  • Состояние находится достаточно близко к потребителю.

Практикумы по теме