Олег Марков
useState в React что это и как использовать
Введение
React помогает создавать интерактивные пользовательские интерфейсы, делая приложения отзывчивыми и удобными для пользователей. Одной из важных особенностей React является управление состоянием компонентов. Раньше для этого требовалось описывать классы и работать с методами жизненного цикла, но теперь для функциональных компонентов можно использовать хуки — специальные функции, которые позволяют использовать возможности React без написания классов.
Хук useState
— один из базовых и самых полезных. Он даёт доступ к управлению внутренним состоянием в функциональных компонентах, делая код проще, короче и понятнее. В этой статье я подробно расскажу, что делает useState, как им пользоваться и на что обратить внимание при работе с этим инструментом.
Что такое useState и зачем он нужен
В React под "состоянием" (state) обычно понимают значения, которые меняются по ходу работы приложения и могут влиять на внешний вид или поведение компонентов. Например: текст в поле ввода, количество нажатий на кнопку, выбранная вкладка и так далее.
До появления хуков, если вам нужно было хранить и обновлять такие значения, приходилось использовать классы:
// Классический компонент с состоянием
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // состояние компонента
}
render() {
return (
<div>
<p>Счетчик: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Увеличить
</button>
</div>
);
}
}
Теперь с помощью хуков мы можем сделать то же самое во много раз проще, не используя классы — только функции.
useState
— это React Hook, который позволяет добавлять состояние в функциональные компоненты. Это один из самых основных и часто используемых хуков в React. Он позволяет компонентам "запоминать" информацию между рендерами и обновлять ее при необходимости. Если вы хотите детальнее изучить useState
в React и научиться его правильно использовать — приходите на наш большой курс Основы React, React Router и Redux Toolkit. На курсе 177 уроков и 17 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.
Как работает useState
Хук useState
позволяет объявить переменную состояния прямо внутри функционального компонента и предоставляет функцию для её обновления. Примерно так:
import React, { useState } from 'react';
function Counter() {
// Объявляем переменную состояния "count" со стартовым значением 0
// setCount — функция для обновления значения count
const [count, setCount] = useState(0);
return (
<div>
<p>Счетчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}
Смотрите, здесь я использую деструктуризацию массива:
- Первая часть (
count
) — текущее значение состояния. - Вторая часть (
setCount
) — функция для изменения этого значения.
useState всегда возвращает именно массив из двух элементов.
Формат и соглашения по именованию
Обычно такую пару называют [something, setSomething]
. Это негласный стандарт, следуя которому код становится понятнее для других разработчиков.
Как использовать useState: развернутый разбор
Импортирование useState
Прежде чем использовать useState, его нужно импортировать из пакета react:
import React, { useState } from 'react';
Без этого создать состояние НЕ получится — React скажет, что useState "undefined".
Инициализация состояния
useState принимает в качестве параметра стартовое значение состояния — это может быть строка, число, объект, массив, булево значение, или даже функция.
// Примеры различных стартовых значений:
const [count, setCount] = useState(0); // число
const [text, setText] = useState('Hello'); // строка
const [checked, setChecked] = useState(false); // булево
const [items, setItems] = useState([]); // массив
Выбор стартового значения зависит от вашей задачи.
Как изменять состояние с помощью функции обновления
Материализуем знание на практике. Вот пример текстового поля, которое обновляет состояние при каждом вводе символа:
function InputExample() {
// Инициализируем состояние для текста
const [text, setText] = useState('');
// Обработчик события изменения input
function handleChange(event) {
setText(event.target.value);
}
return (
<div>
<input value={text} onChange={handleChange} /> {/* Привязываем значение и обработчик */}
<p>Вы ввели: {text}</p>
</div>
);
}
Когда пользователь вводит что-то в поле, функция setText запоминает это, а компонент повторно отрисовывается с новым значением.
Передача функции в setState для получения актуального значения
Бывают случаи, когда новое состояние зависит от предыдущего значения. Рекомендуется передавать в функцию обновления не просто новое значение, а функцию с предыдущим значением как аргументом:
const [count, setCount] = useState(0);
function handleClick() {
// Лучше так:
setCount(prevCount => prevCount + 1); // prevCount гарантированно актуально
}
Это избавляет от проблем с устаревшими значениями, например, при быстром клике несколько раз подряд.
Состояния с объектами и массивами
useState может хранить в себе даже сложные структуры. Важно помнить, что при обновлении объекта или массива, вы должны создавать новый экземпляр, а не менять старый "по месту", чтобы React правильно отследил изменения.
Посмотрите на пример с состоянием, хранящим объект:
function UserForm() {
const [user, setUser] = useState({ name: '', age: '' });
// handleChange для разных полей
function handleChange(event) {
const { name, value } = event.target;
// Делаем копию объекта, обновляя только нужное поле
setUser(prevUser => ({
...prevUser,
[name]: value,
}));
}
return (
<form>
<input
name="name"
value={user.name}
onChange={handleChange}
placeholder="Ваше имя"
/>
<input
name="age"
value={user.age}
onChange={handleChange}
placeholder="Возраст"
/>
<p>Имя: {user.name}, Возраст: {user.age}</p>
</form>
);
}
Обратите внимание, что мы не изменяем исходный объект напрямую, а всегда создаём новый с помощью оператора spread (...
).
Аналогично для массивов:
function ListExample() {
const [items, setItems] = useState([]);
function addItem() {
setItems(prevItems => [...prevItems, `Элемент ${prevItems.length + 1}`]);
}
return (
<div>
<button onClick={addItem}>Добавить</button>
<ul>
{items.map((item, idx) => <li key={idx}>{item}</li>)}
</ul>
</div>
);
}
Инициализация состояния через функцию
Если стартовое значение вычисляется сложно, можно передать в useState функцию, которая вернет начальное значение. Эта функция будет вызвана только при первом рендере:
// Функция-инициализатор
function computeInitialValue() {
// Здесь могла бы быть какая-то тяжелая операция
return Math.floor(Math.random() * 1000);
}
const [value, setValue] = useState(computeInitialValue); // Передаем саму функцию!
Так React не будет вызывать тяжелую функцию каждый раз, когда компонент обновляется — только при монтировании.
Обновление состояния «по месту» или создание новых объектов?
React не отслеживает изменения по ссылке внутри сложных структур, поэтому всегда создавайте копию нового значения для объектов и массивов, иначе обновление может не произойти!
Плохой пример (НЕ делать!):
setItems(items.push(newItem)); // мутирует старый массив!
Хороший пример:
setItems(prevItems => [...prevItems, newItem]); // создает новый массив
Как работает повторная отрисовка при изменении useState
Когда вы вызываете функцию обновления (например, setCount), React запоминает новое значение, а затем инициирует повторную отрисовку (render) компонента. Во время рендера компонент использует новое значение состояния. Это и есть реактивность: при изменении данных интерфейс обновляется сам.
Важно: изменение состояния перезапустит только тот компонент, где оно объявлено, и его дочерние компоненты. Родитель не будет перерисован "обратно наверх".
Несколько useState в одном компоненте
Ничто не мешает вам использовать несколько независимых состояний в одном компоненте:
function MultipleStates() {
const [name, setName] = useState('');
const [age, setAge] = useState('');
const [isActive, setIsActive] = useState(false);
// Здесь каждый setName/setAge/setIsActive обновляет свою часть состояния
// и не влияет напрямую на другие
}
Благодаря этому код становится проще — каждый фрагмент состояния управляет только своей логикой и не пересекается с остальными.
Порядок вызова useState и правила хуков
Есть важное правило: вызывать useState и другие хуки можно только на верхнем уровне вашего функционального компонента, а не внутри условий, циклов или вложенных функций.
// ПРАВИЛЬНО:
function SomeComponent() {
const [flag, setFlag] = useState(false);
// ...код
}
// НЕПРАВИЛЬНО! Не делать так!
if (condition) {
const [state, setState] = useState(true);
}
Это правило нужно соблюдать, чтобы React всегда знал, какой state за каким идёт. Иначе порядок может нарушиться, и React "перепутает" состояния, возникнет ошибка.
Использование useState в формах и взаимодействии с пользователем
useState часто применяют для создания управляемых форм:
function LoginForm() {
const [login, setLogin] = useState('');
const [password, setPassword] = useState('');
function handleLoginChange(e) {
setLogin(e.target.value);
}
function handlePasswordChange(e) {
setPassword(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
// Здесь вы можете работать с актуальными данными полей
// Например, отправить их на сервер
}
return (
<form onSubmit={handleSubmit}>
<input value={login} onChange={handleLoginChange} placeholder="Логин" />
<input value={password} onChange={handlePasswordChange} type="password" placeholder="Пароль" />
<button>Войти</button>
</form>
);
}
Каждый ввод пользователя вызывает функцию обновления, и все значения всегда "свежие".
Кодовые практики и распространённые ошибки
Нельзя напрямую изменять состояние
// ПЛОХО! НЕ делать так:
count = count + 1; // Не работает — только через setCount!
// ПРАВИЛЬНО:
setCount(count + 1); // Только такая запись сработает
Не следует копировать и сохранять ссылку на функцию setState вне компонента
setCount и другие функции обновления работают корректно только внутри контекста компонента, для которого были созданы.
Не используйте useState для хранения "вычисляемых" данных
Если вы можете вычислить значение на лету из других переменных, не стоит хранить его отдельно в useState. Используйте простые выражения или хук useMemo
.
Ленивая инициализация useState
Если результат вычислений для начального значения ресурсоёмкий, есть возможность сделать так, чтобы он сработал только один раз:
const [expensiveValue, setExpensiveValue] = useState(() => {
// Это вызовется ОДИН раз только при первом рендере
return сложная_функция();
});
Такой синтаксис помогает избегать лишней нагрузки при каждом обновлении компонента.
useState и асинхронность
Состояние в React не обновляется мгновенно. После вызова setCount, новое значение станет доступно только после следующего рендера компонента.
Если вам требуется получить новое значение сразу после обновления, используйте эффект useEffect:
const [value, setValue] = useState(0);
useEffect(() => {
// Этот блок вызовется после очередного обновления value
// Здесь уже value содержит новое значение
console.log('Новое значение:', value);
}, [value]);
Пример полноценного счетчика с увеличением и сбросом
Покажу вам, как пример полного кода для счетчика, который можно копировать и использовать для тренировки:
import React, { useState } from 'react';
function FullCounter() {
const [count, setCount] = useState(0);
function increment() {
setCount(c => c + 1); // Используем функцию для обновления
}
function reset() {
setCount(0); // Сбрасываем счетчик к начальному значению
}
return (
<div>
<h2>Счетчик: {count}</h2>
<button onClick={increment}>Прибавить</button>
<button onClick={reset}>Сбросить</button>
</div>
);
}
Как видите, вся логика управления состоянием состоит из пары строчек. Это основное преимущество useState!
Заключение
useState полностью изменил подход к созданию динамических, интерактивных интерфейсов на React. Теперь не обязательно использовать классы — вы пишете компоненты как чистые функции, и подключаете любое нужное количество состояний через хуки. Хук useState прост, но при этом очень мощен: с ним любая часть интерфейса может реагировать на действия пользователя или другие события мгновенно и предсказуемо.
С учетом лучших практик и типичных сценариев использования, вы сможете строить удобные формы, списки, фильтры, счетчики, табы и любые другие интерфейсы — и делать это проще и быстрее, чем раньше.
useState
позволяет управлять состоянием компонента. Для создания сложных приложений требуется умение управлять состоянием всего приложения и организовывать навигацию. Курс Основы React, React Router и Redux Toolkit предоставит вам комплексные знания. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в основы React уже сегодня.
Частозадаваемые технические вопросы по теме useState
Как сбросить состояние к начальному значению?
Чтобы сбросить состояние, вызовите функцию обновления с исходным значением:
setCount(0); // Например, если начальное значение — 0
Для массивов или объектов просто передайте начальное состояние, такое какое вы указывали в useState.
Могу ли я хранить в useState функцию?
Да, можете, но помните: useState принимает функцию только для "ленивой" инициализации (чтобы вернуть первоначальное значение). Если вам надо хранить именно функцию (как переменную), передайте ее в useState обернув в анонимную функцию:
const [callback, setCallback] = useState(() => () => console.log('Hi!'));
Почему состояние не обновляется сразу после вызова setState?
Обновление состояния происходит асинхронно — React сначала запланирует новый рендер, а только после него обновит значение. Если вам нужно получить новое значение после обновления, используйте useEffect.
Как обновлять вложенные структуры (массивы/объекты) в useState?
Для обновления объектов и массивов создавайте новые экземпляры на основе предыдущего значения, используя spread-оператор:
setUser(prev => ({ ...prev, age: prev.age + 1 }));
setList(prev => [...prev, newItem]);
Можно ли менять состояние внутри useEffect?
Да, но только если уверены, что это не приведет к бесконечному циклу. Обновление состояния вызывает повторный рендер и перезапускает useEffect, если это значение указано в его зависимостях. Чтобы избежать зацикливания, корректно настраивайте массив зависимостей второго аргумента useEffect.
Постройте личный план изучения React до уровня Middle — бесплатно!
React — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по React
Лучшие курсы по теме

React и Redux Toolkit
Антон Ларичев
TypeScript с нуля
Антон Ларичев