Управление состоянием компонента
В React состояние управляет тем, как компоненты реагируют на пользовательские действия, данные и события. Это как бариста в кофейне — у него есть "состояние" (количество молока, наполненность кофейного бункера), которое меняется в зависимости от ваших заказов.
Состояние позволяет приложениям быть интерактивными, обновляя интерфейс, когда происходит что-то новое. Например:
- Количество товаров в корзине.
- Цвет кнопки, если вы навели на неё мышь.
- Показ/скрытие выпадающего списка.
В React состояние всегда принадлежит компоненту и существует в рамках его жизненного цикла.
Что такое useState?
useState — это хук (функция), который позволяет функциональным компонентам в React иметь собственное состояние. До появления хуков в React 16.8 состояние можно было использовать только с классовыми компонентами. Теперь же функциональные компоненты стали настоящими рок-звёздами React-а.
Вот базовая структура:
import React, { useState } from 'react';
const Example = () => {
const [state, setState] = useState(initialValue);
return <div>{state}</div>;
};
useState возвращает массив из двух элементов:
- Текущее значение состояния (здесь —
state). - Функция для обновления состояния (здесь —
setState).
Как создать состояние с useState?
Начнём с простого примера. Представим, что мы создаём кнопку, которая считает, сколько раз на неё нажали.
import React, { useState } from 'react';
const Counter = () => {
// Создаем состояние, начальное значение 0
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // Обновление состояния
};
return (
<div>
<p>Вы нажали {count} раз</p>
<button onClick={handleClick}>Нажми меня</button>
</div>
);
};
export default Counter;
Здесь произошло вот что:
- Мы создали состояние
countс начальным значением0. - Использовали
setCountдля обновления значения. - На каждый клик вызывается
handleClick, и счётчик увеличивается.
Особенности useState
- Асинхронность. Обновление состояния может быть асинхронным. Если вы пытаетесь обновить состояние в зависимости от его предыдущего значения, лучше использовать функцию:
setCount((prevCount) => prevCount + 1);Это гарантирует, что обновления будут корректными, даже если в одном событии происходит несколько вызовов
setCount. - Разделение состояний. Каждый вызов
useStateсоздаёт отдельный "ячейку" состояния. Это позволяет разделять логику:const [likes, setLikes] = useState(0); const [comments, setComments] = useState([]); Начальное значение.
useStateпринимает начальное значение состояния и может быть чем угодно: числом, строкой, массивом, объектом.
Пример: Состояние с объектом
Состояние может быть не только простым числом или строкой, но и более сложной структурой, например, объектом. Представим, что мы создаём форму, в которой пользователь может ввести имя и email.
import React, { useState } from 'react';
const Form = () => {
const [formState, setFormState] = useState({ name: '', email: '' });
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormState((prevState) => ({
...prevState, // Сохраняем предыдущие значения
[name]: value, // Обновляем поле по имени
}));
};
return (
<form>
<label>
Имя:
<input
type="text"
name="name"
value={formState.name}
onChange={handleChange}
/>
</label>
<label>
Email:
<input
type="email"
name="email"
value={formState.email}
onChange={handleChange}
/>
</label>
<p>
Ваше имя: {formState.name}, ваш email: {formState.email}
</p>
</form>
);
};
export default Form;
Здесь мы используем объект для хранения данных в форме. Используя спред-оператор ...prevState, мы сохраняем предыдущие значения и обновляем только нужное поле.
Что делать со сложными структурами данных?
Если состояние представляет собой массив или объект, можно использовать методы массивов или создавать копию объекта, чтобы избежать мутаций.
Пример: добавление элемента в массив
const [items, setItems] = useState<string[]>([]);
const addItem = () => {
setItems((prevItems) => [...prevItems, `Item ${prevItems.length + 1}`]);
};
Пример: удаление элемента из массива
const removeItem = (index: number) => {
setItems((prevItems) => prevItems.filter((_, i) => i !== index));
};
Это важно, потому что состояние должно быть иммутабельным. React использует сравнение ссылок для определения того, изменилось ли состояние.
Типизация состояния с useState
TypeScript автоматически выведет тип данных состояния из переданного значения. Но иногда вам нужно указать тип явно.
Пример: типизация строки
const [name, setName] = useState<string>('');
Пример: типизация массива
const [numbers, setNumbers] = useState<number[]>([]);
Пример: типизация объекта
interface User {
name: string;
age: number;
}
const [user, setUser] = useState<User>({ name: '', age: 0 });
Если вы заметили, мы используем интерфейсы для объектов. Это делает код более читаемым и безопасным.
Типичные ошибки при работе с useState
- Забыть использовать функцию обновления состояния. Если попытаетесь напрямую изменить состояние
state = newValue, React будет игнорировать изменения. - Мутация состояния. Если вы изменяете массив или объект без создания копии, React может не отобразить изменения. Всегда используйте методы
.map,.filter, или спред-оператор. - Неправильная обработка асинхронных обновлений. Если обновления зависят от предыдущего состояния, используйте функцию в
setState.
Практическое применение
Каждый React-разработчик сталкивается с задачами, где нужно запоминать и изменять данные. Использование useState — это основа взаимодействия с пользовательским интерфейсом. Например:
- Управление формами (ввод текста, переключатели).
- Создание интерактивных элементов (счётчики, переключатели тем).
- Реализация простой логики (например, модальные окна).
// Итоговое мини-приложение: Управление темой
import React, { useState } from 'react';
const ThemeSwitcher = () => {
const [isDarkMode, setIsDarkMode] = useState(false);
const toggleTheme = () => {
setIsDarkMode((prev) => !prev);
};
return (
<div style={{ backgroundColor: isDarkMode ? '#333' : '#fff', color: isDarkMode ? '#fff' : '#000', padding: '20px' }}>
<p>{isDarkMode ? 'Темная тема' : 'Светлая тема'}</p>
<button onClick={toggleTheme}>Переключить тему</button>
</div>
);
};
export default ThemeSwitcher;
Поздравляю! Мы не просто меняем тему, мы обучаемся управлению состоянием на лету! Теперь вы готовы к ещё более сложным задачам. 😉
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ