useState — базовый хук React для хранения данных внутри компонента. Если значение влияет на то, что пользователь видит на экране, чаще всего это состояние.
- Синтаксис useState
- Основные приёмы useState
- Пример: счётчик
- Обновление на основе предыдущего значения
- Форма на useState
- Состояние массива
- Типичные ошибки
- Мини-проект: корзина товаров
- Как выбирать форму состояния
- Проверка понимания
- Как понять, что состояние выбрано правильно
- Проверка перед сохранением в state
- Типичный лишний state
- Упражнение для закрепления
- Что изучить дальше по React
Синтаксис useState
import { useState } from 'react';
const [value, setValue] = useState(initialValue);
useState возвращает два значения: текущее состояние и функцию для его изменения. После вызова setValue React заново рендерит компонент и показывает новый интерфейс.
Основные приёмы useState
useState полезнее всего разбирать на простых интерактивных задачах: счётчик, форма, массив элементов и обновление на основе предыдущего значения. Эти приёмы потом повторяются в корзинах, фильтрах, вкладках и модальных окнах.
Не спешите переходить к Redux, пока эти базовые операции не стали привычными. Большая часть локальной интерактивности в React по-прежнему спокойно решается useState без глобального хранилища.
Пример: счётчик
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Значение: {count}</p>
<button onClick={() => setCount(count + 1)}>Увеличить</button>
<button onClick={() => setCount(0)}>Сбросить</button>
</div>
);
}
Обновление на основе предыдущего значения
Если новое состояние зависит от старого, используйте функцию. Это особенно важно, когда несколько обновлений идут подряд.
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
Форма на useState
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
function handleSubmit(event) {
event.preventDefault();
console.log({ email, password });
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
<button>Войти</button>
</form>
);
}
Состояние массива
Нельзя менять массив напрямую через push или splice. Создавайте новый массив, чтобы React увидел изменение.
function TodoList() {
const [todos, setTodos] = useState([]);
function addTodo(text) {
const newTodo = { id: Date.now(), text, done: false };
setTodos(prevTodos => [...prevTodos, newTodo]);
}
function toggleTodo(id) {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
);
}
}
Типичные ошибки
- Менять объект или массив напрямую.
- Ждать, что состояние изменится сразу в той же строке после setState.
- Хранить в state данные, которые можно вычислить из других значений.
- Создавать слишком много независимых state, когда нужен один объект формы.
Мини-проект: корзина товаров
Корзина хорошо показывает useState, потому что состояние — это массив объектов, а действия меняют количество и состав товаров.
function Cart() {
const [items, setItems] = useState([
{ id: 1, title: 'Курс React', price: 2900, count: 1 },
]);
function increase(id) {
setItems(prev =>
prev.map(item =>
item.id === id ? { ...item, count: item.count + 1 } : item
)
);
}
function remove(id) {
setItems(prev => prev.filter(item => item.id !== id));
}
const total = items.reduce((sum, item) => sum + item.price * item.count, 0);
return (
<section>
{items.map(item => (
<article key={item.id}>
<b>{item.title}</b>
<span>{item.count} шт.</span>
<button onClick={() => increase(item.id)}>+</button>
<button onClick={() => remove(item.id)}>Удалить</button>
</article>
))}
<p>Итого: {total} руб.</p>
</section>
);
}
В этом примере total не хранится в state, потому что его можно вычислить из items. Это важное правило: не дублируйте производные данные в состоянии, иначе однажды они начнут расходиться.
Как выбирать форму состояния
- Если значения меняются независимо, можно хранить их разными useState.
- Если значения всегда меняются вместе, удобнее объект.
- Если есть повторяющиеся элементы, нужен массив.
- Если обновления стали сложными, рассмотрите useReducer или Redux Toolkit.
Проверка понимания
Попробуйте добавить кнопку «Очистить корзину», ограничение количества до 10 и сообщение «Корзина пуста». Если вы можете сделать это без прямой мутации массива, базовый useState понятен.
Это упражнение хорошо тем, что сразу проверяет три вещи: обновление массива без мутации, вычисление производных значений и реакцию интерфейса на пустое состояние. Если все три пункта получаются, база useState уже крепкая.
Как понять, что состояние выбрано правильно
Главное правило useState: храните минимальный набор данных, от которого зависит интерфейс. Всё, что можно надёжно вычислить из уже имеющегося state, не нужно дублировать.
Чем меньше дублирования в state, тем меньше расхождений в интерфейсе. Например, итоговую сумму корзины лучше вычислять из массива товаров, а не хранить отдельно рядом с теми же данными.
Проверка перед сохранением в state
- это значение меняется со временем?
- оно влияет на то, что пользователь видит?
- его нельзя вычислить из других значений?
- оно принадлежит именно этому компоненту?
- если страницу перезагрузить, его можно потерять?
Типичный лишний state
Например, total в корзине часто не нужно хранить отдельно. Его можно вычислить из items через reduce. Если хранить и items, и total, однажды они могут разъехаться.
Упражнение для закрепления
Сделайте корзину: добавить товар, увеличить количество, удалить товар, показать итоговую сумму. Если итог считается из массива, а массив не мутируется напрямую, useState используется правильно.



