Урок 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 нет вычисляемых дубликатов.
- Состояние находится достаточно близко к потребителю.
Практикумы по теме
- Два экрана одним состоянием — Для двух видов одной маленькой программы маршрутизатор не обязателен.
- Кнопка в React меняет состояние — Компонент уже умеет хранить state. Кнопка — самый прямой способ его поменять.