useState — компонент с памятью
Компонент с props — статичен: что передали, то показал. useState — это «память» компонента. Счётчик, текст в форме, открыто ли меню — всё это state. Когда state меняется, React перерисовывает компонент.
Базовый useState
это деструктуризация массива из двух элементов: текущее значение и функция-сеттер. 0 — начальное значение. При вызове setCount(5) — React: 1) обновляет state на 5, 2) ПЕРЕЗАПУСКАЕТ функцию компонента, 3) перерисовывает DOM с новым значением. Никогда не меняй state напрямую count = 5 — ничего не произойдёт, нужно ИМЕННО setCount.
Несколько useState
В одном компоненте можно много useState — для каждого «куска памяти» свой. Например в форме регистрации:
Каждый изолирован. Если данные связаны (объект user с многими полями) — лучше один useState с объектом, и обновлять через spread:
Реактивность — главное правило
НИКОГДА не мутируй state. arr.push(x) — не сработает (тот же массив, React не понимает). Правильно: setArr([...arr, x]). С объектом: obj.name = "x" плохо, setObj({...obj, name: "x"}) хорошо. Это потому что React сравнивает СТАРОЕ и НОВОЕ значение по ссылке. Та же ссылка — нет изменений → нет перерисовки.
События — onClick, onChange
Связь UI с state — через события.
Для полей ввода:
Это называется «controlled input» — value привязано к state, onChange его обновляет. Без этого React теряет контроль над содержимым поля.
Функция в setter — для безопасности
Когда новое значение зависит от старого — используй колбэк-форму: setCount(c => c + 1) вместо setCount(count + 1). Это ВСЕГДА получит АКТУАЛЬНОЕ значение, даже если несколько setCount подряд. Если просто count + 1 — может получиться race condition в редких случаях.
import { useState } from 'react';
// === Простой счётчик ===
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<p>Текущее значение: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(c => c - 1)}>-1</button>
<button onClick={() => setCount(0)}>Сброс</button>
</>
);
}
// === Контролируемый input ===
function NameForm() {
const [name, setName] = useState('');
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Ваше имя"
/>
<p>Привет, {name || 'Гость'}!</p>
</form>
);
}
// === State объектом ===
function ProfileForm() {
const [user, setUser] = useState({ name: '', email: '', age: 0 });
function update(field, value) {
setUser({ ...user, [field]: value }); // СПРЕД + замена одного поля
}
return (
<form>
<input
placeholder="Имя"
value={user.name}
onChange={(e) => update('name', e.target.value)}
/>
<input
placeholder="Email"
type="email"
value={user.email}
onChange={(e) => update('email', e.target.value)}
/>
<input
placeholder="Возраст"
type="number"
value={user.age}
onChange={(e) => update('age', +e.target.value)}
/>
<pre>{JSON.stringify(user, null, 2)}</pre>
</form>
);
}
// === Список с добавлением и удалением ===
function TodoList() {
const [items, setItems] = useState<string[]>([]);
const [text, setText] = useState('');
function add() {
if (!text.trim()) return;
setItems([...items, text]); // НОВЫЙ массив, не push
setText('');
}
function remove(idx) {
setItems(items.filter((_, i) => i !== idx)); // НОВЫЙ массив
}
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={add}>Добавить</button>
<ul>
{items.map((item, i) => (
<li key={i}>
{item} <button onClick={() => remove(i)}>×</button>
</li>
))}
</ul>
</div>
);
}
// === Тогглы и булевы ===
function Toggle() {
const [on, setOn] = useState(false);
return (
<button onClick={() => setOn(!on)}>
{on ? '🌞 День' : '🌙 Ночь'}
</button>
);
}useState на разных задачах