Как работает useState? Приведите пример.

Хук useState — это один из базовых хуков в React, предназначенный для добавления состояния в функциональные компоненты. До появления хуков в React (в версии 16.8) состояние можно было использовать только в классовых компонентах. С помощью useState функциональные компоненты получают возможность хранить и изменять данные между рендерами, не превращаясь в классы.

1. Что делает useState

Хук useState:

  • Инициализирует локальное состояние компонента.

  • Возвращает массив из двух элементов: текущего значения состояния и функции, с помощью которой можно изменить это значение.

  • При вызове функции обновления состояния React повторно рендерит компонент, чтобы отобразить новое состояние.

2. Синтаксис useState

const [state, setState] = useState(initialValue);

  • state — текущее значение состояния.

  • setState — функция, которая позволяет обновить значение.

  • initialValue — начальное значение состояния (может быть примитивом, объектом, массивом, функцией и т.д.).

3. Простой пример

import React, { useState } from 'react';
function Counter() {
const \[count, setCount\] = useState(0);
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={() => setCount(count + 1)}>Увеличить</button>
<button onClick={() => setCount(count - 1)}>Уменьшить</button>
<button onClick={() => setCount(0)}>Сброс</button>
</div>
);
}
  • useState(0) создаёт переменную count, инициализированную значением 0.

  • setCount изменяет значение count, а React перерисовывает компонент с новым значением.

  • Каждое нажатие кнопки вызывает функцию, которая изменяет состояние.

4. Особенности работы useState

a. Состояние сохраняется между рендерами

Даже если компонент перерисовывается (вызван новый рендер), состояние, заданное через useState, не теряется:

function Example() {
const \[value, setValue\] = useState('hello');
// при каждом рендере value сохраняется и используется
}

b. Обновление состояния вызывает повторный рендер

Когда вызывается setState, React повторно вызывает компонент, чтобы отобразить новое состояние. Обновление не происходит мгновенно — это асинхронный процесс, React может оптимизировать несколько обновлений.

setCount(count + 1); // инициирует ререндер с новым значением

c. Можно использовать несколько состояний

В одном компоненте можно вызвать useState несколько раз — каждое состояние будет независимым:

const \[name, setName\] = useState('');
const \[age, setAge\] = useState(0);
const \[isVisible, setIsVisible\] = useState(true);

5. Передача функции вместо значения

Если начальное значение должно вычисляться, можно передать функцию, которая будет вызвана один раз при инициализации (ленивая инициализация):

const \[count, setCount\] = useState(() => {
console.log('Вычисляем начальное значение');
return 10;
});

Это полезно при тяжёлых вычислениях — чтобы они выполнялись один раз, а не при каждом рендере.

6. Обновление состояния на основе предыдущего

Из-за асинхронности обновлений лучше использовать функциональную форму setState, когда новое значение зависит от старого:

setCount(prevCount => prevCount + 1);

Такой способ надёжнее, особенно если обновлений несколько подряд или они происходят внутри setTimeout, setInterval и т.п.

7. Работа с объектами и массивами в useState

a. Объекты

Если состояние — объект, при обновлении нужно явно сохранять неизменные свойства:

const \[user, setUser\] = useState({ name: 'Анна', age: 25 });
setUser(prevUser => ({
...prevUser,
age: 26
}));

Если просто вызвать setUser({ age: 26 }), то поле name потеряется — новый объект полностью заменит старый.

b. Массивы

const \[todos, setTodos\] = useState(\[\]);
setTodos(prevTodos => \[...prevTodos, { id: 1, text: 'Новая задача' }\]);

Любое изменение — добавление, удаление, обновление — должно производиться через копирование массива и возврат нового.

8. Поведение при повторной установке того же значения

Если вызвать setState с тем же значением, которое уже установлено, React не будет ререндерить компонент:

const \[count, setCount\] = useState(5);
setCount(5); // ничего не произойдёт

Это сделано для оптимизации.

9. useState и ререндер

Важно понимать: состояние привязано к порядку вызова хуков. Если в компоненте меняется количество или порядок вызовов useState, возникнет ошибка.

function Example() {
if (someCondition) {
const \[x, setX\] = useState(0); //  так делать нельзя
}
}

Хуки должны вызываться на верхнем уровне компонента, без условий и циклов.

10. Пример с инпутом (управляемый компонент)

function NameForm() {
const \[name, setName\] = useState('');
const handleChange = (e) => {
setName(e.target.value);
};
return (
<div>
<input type="text" value={name} onChange={handleChange} />
<p>Ваше имя: {name}</p>
</div>
);
}

Значение name хранится в состоянии и обновляется при каждом вводе. Таким образом, React полностью управляет значением инпута — это контролируемый компонент.

11. Совместимость с другими хуками

useState часто используется вместе с другими хуками:

  • useEffect — реагирует на изменение состояния.

  • useRef — работает с DOM без потери состояния.

  • useReducer — альтернатива для сложных состояний.

12. Когда не использовать useState

  • Когда данные не должны сохраняться между рендерами — лучше использовать обычные переменные.

  • Когда нужно взаимодействовать с внешними хранилищами (например, Redux или Zustand).

  • Когда нужно глобальное состояние, доступное из разных компонентов.

Хук useState — основа для управления данными внутри компонента. Он делает функциональные компоненты «живыми», позволяя им изменять своё поведение и внешний вид в зависимости от пользовательских действий или логики.