State: внутреннее состояние
State (состояние) компонента — данные, которые компонент хранит и изменяет сам; при изменении state React перерендеривает компонент с новыми значениями.
Зачем нужно
Без state компоненты были бы чисто статическими шаблонами. State — механизм «памяти» компонента: он помнит, открыт ли дропдаун, что пользователь ввёл в поле, сколько элементов в корзине. Понимание того, что изменение state запускает перерендер, — ключ к работе с React и отладке неожиданного поведения.
Где используется
- Значение инпута, текстового поля
- Открыт/закрыт: модалки, аккордеоны, дропдауны, тултипы
- Текущий шаг многошагового процесса (wizard, onboarding)
- Данные, загружаемые с сервера и отображаемые в компоненте
- Любые UI-флаги: isLoading, isError, isExpanded
useState: базовое состояние
import { useState } from 'react';
function Counter() {
// useState возвращает [текущее значение, функция-сеттер]
const [count, setCount] = useState(0);
return (
<div>
<p>Счётчик: {count}</p>
{/* Функциональное обновление — безопасно при асинхронности */}
<button onClick={ => setCount(prev => prev + 1)}>+</button>
<button onClick={ => setCount(prev => prev - 1)}>-</button>
<button onClick={ => setCount(0)}>Сброс</button>
</div>
);
}
function ToggleButton() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={ => setIsOpen(prev => !prev)}>
{isOpen ? 'Закрыть' : 'Открыть'}
</button>
{isOpen && <div className="dropdown">Содержимое</div>}
</div>
);
}
Состояние объекта
function ProfileForm() {
const [form, setForm] = useState({
name: '',
email: '',
bio: '',
});
// Важно: spread предыдущего состояния — не мутировать напрямую
const handleChange = (field) => (e) => {
setForm(prev => ({
...prev,
[field]: e.target.value,
}));
};
return (
<form>
<input value={form.name} onChange={handleChange('name')} placeholder="Имя" />
<input value={form.email} onChange={handleChange('email')} placeholder="Email" />
<textarea value={form.bio} onChange={handleChange('bio')} placeholder="О себе" />
</form>
);
}
useReducer — сложное состояние
import { useReducer } from 'react';
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'setStep':
return { ...state, step: action.payload };
case 'reset':
return initialState;
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Счётчик: {state.count} (шаг: {state.step})</p>
<button onClick={ => dispatch({ type: 'increment' })}>+</button>
<button onClick={ => dispatch({ type: 'decrement' })}>-</button>
<button onClick={ => dispatch({ type: 'reset' })}>Сброс</button>
<input
type="number"
value={state.step}
onChange={(e) => dispatch({ type: 'setStep', payload: Number(e.target.value) })}
/>
</div>
);
}
Частые ошибки
- Прямая мутация state —
state.items.push(item)не вызывает перерендер; всегда возвращайте новый объект через spread илиmap/filter. - State для данных, которые можно вычислить — если значение зависит от другого state, вычисляйте его в рендере, не дублируйте в отдельный state.
- Избыточное состояние — хранить в state форматированную дату, если в state уже есть
Date-объект — дублирование. - Асинхронное чтение state — после
setCount(count + 1)переменнаяcountв том же обработчике ещё старая; используйте функциональное обновление.
Связанные темы
- _MOC SPA
- Локальное vs глобальное состояние
- Props -- передача данных
- Жизненный цикл компонента
- Context API -- обзор