Состояние: useState
Данные, которые меняются
Props приходят снаружи и не меняются компонентом. Но у интерфейса есть и своя память: открыто ли меню, что введено в поле, сколько раз нажали кнопку, какая вкладка выбрана. Это состояние (state). Когда состояние меняется, React сам перерисовывает компонент с новыми значениями — не нужно искать элементы и менять их текст вручную.
useState
Состояние создаётся хуком useState:
const { useState } = React; // в проекте: import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Нажато: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
useState(0)— начальное значение, используется только при первом показе.- Возвращает пару: текущее значение
countи функциюsetCount, чтобы его поменять. Имена выбираешь сам, приняточто-тоиsetЧто-то. setCount(5)запоминает новое значение и просит React перерисовать компонент. При следующем вызове функцииCounteruseStateвернёт уже5.
У каждого экземпляра компонента своё состояние: два <Counter /> на странице считают независимо.
Почему не обычная переменная
function Broken() {
let count = 0; // при каждой перерисовке снова 0
return <button onClick={() => { count++; }}>{count}</button>; // и React не узнает об изменении
}
Обычная переменная создаётся заново при каждом вызове функции-компонента и не говорит React, что
пора перерисовать. Поэтому всё, что меняется и должно быть видно на экране, хранят в useState.
События в React
Обработчики пишутся как props в camelCase: onClick, onChange, onSubmit, onMouseEnter,
onKeyDown. В них передают функцию, а не её вызов:
<button onClick={handleClick}>OK</button> // правильно — функция
<button onClick={() => setOpen(true)}>Открыть</button> // правильно — стрелка
<button onClick={setOpen(true)}>Открыть</button> // ошибка: вызовется сразу при отрисовке
Обновление по предыдущему значению
setCount(count + 1) берёт count из текущей отрисовки. Если обновить несколько раз подряд, все
вызовы увидят одно и то же старое значение:
setCount(count + 1);
setCount(count + 1); // count всё ещё старый → итог +1, а не +2
Надёжный способ — передать функцию, которая получает актуальное значение:
setCount((c) => c + 1);
setCount((c) => c + 1); // итог +2
Используй форму с функцией всегда, когда новое значение считается из старого.
Логическое состояние
Открыто / закрыто, включено / выключено — true и false:
const [dark, setDark] = useState(false);
<button onClick={() => setDark((d) => !d)}>
{dark ? "Светлая тема" : "Тёмная тема"}
</button>
Объекты и массивы в состоянии
Состояние нельзя менять на месте (user.name = ..., items.push(...)): React сравнивает старое
и новое значение, и если это тот же объект, он решит, что ничего не изменилось. Всегда создавай
новый объект или массив:
const [user, setUser] = useState({ name: "Аня", age: 20 });
setUser({ ...user, age: user.age + 1 }); // копия с изменённым полем
const [items, setItems] = useState([]);
setItems([...items, newItem]); // добавить
setItems(items.filter((i) => i.id !== id)); // удалить
setItems(items.map((i) => (i.id === id ? { ...i, done: !i.done } : i))); // изменить один
Это те же ..., filter и map из курса JavaScript — в React они используются постоянно.
Неактивные кнопки
Логические атрибуты передаются выражением: <button disabled={count === 0}>. Когда условие ложно,
React уберёт атрибут, когда истинно — добавит.
Правила хуков
useState и другие функции, начинающиеся с use, — это хуки. Два правила:
- Вызывать только на верхнем уровне компонента — не внутри
if, циклов и вложенных функций. - Вызывать только в компонентах (или в своих хуках), не в обычных функциях.
React различает состояния по порядку вызова хуков, поэтому порядок должен быть одинаковым при каждой отрисовке.
Итог
const [value, setValue] = useState(начальное)— память компонента;setValueперерисовывает.- Обработчики:
onClick={функция}, не вызов. - Новое из старого —
setValue((old) => ...). - Объекты и массивы не меняем на месте — создаём копии через
...,map,filter. - Хуки — только на верхнем уровне компонента.