Недавно на аудите одного CRM-подобного дашборда я насчитал 47 вызовов useState в одном роуте. Клиент был уверен, что проблема в управлении состоянием и хотел внедрить Zustand. Но после анализа стало ясно: проблема не в выборе библиотеки, а в том, где и как хранится состояние.
useState как молоток: когда всё выглядит как гвоздь
Когда начинаешь изучать React, useState — это первый инструмент для работы с состоянием. И это здорово, потому что он простой и понятный. Но именно эта простота становится проблемой на практике. Разработчики начинают использовать useState для всего подряд, даже там, где это не нужно.
В моём случае из 47 вызовов только 11 действительно хранили UI-состояние. Остальное было:
- Данные с сервера
- Параметры URL
- Значения форм
- Производные значения (например, результаты вычислений)
И это классическая ошибка: использовать useState для данных, которые не являются состоянием в классическом понимании.
Фильтры, сортировка и страницы: почему это не состояние
Один из ярких примеров — фильтры, сортировка и пагинация. В проекте они хранились в useState. В итоге, если пользователь сортировал таблицу, переходил на третью страницу, а затем возвращался назад, все его действия сбрасывались. Для пользователя это выглядело как баг, хотя технически это была “фича” работы с состоянием.
Решение? Переместить всё в searchParams. Это не только сделало поведение предсказуемым, но и восстановило работу кнопки “Назад” без дополнительных усилий.
Серверные данные в useState: боль и страдания
Ещё один пример — хранение данных с сервера в useState. В проекте это выглядело так:
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch('/api/data')
.then(response => {
if (!response.ok) {
setData(response.body); // Ой!
} else {
return response.json();
}
})
.then(data => setData(data))
.finally(() => setLoading(false));
}, []);
Здесь сразу несколько проблем:
- Нет кэширования: данные загружаются каждый раз, даже если они уже есть.
- Нет дедупликации запросов.
- Ошибки обрабатываются некорректно: если сервер возвращает 404 или 500, данные устанавливаются в тело ошибки.
Состояние — это не только useState
Основная мысль, которую я хочу донести: проблема не в том, что useState плохой инструмент. Проблема в том, что его используют не по назначению. Состояние должно быть там, где оно логически принадлежит:
- UI-состояние:
useStateилиuseReducer. - URL-параметры:
searchParams. - Серверные данные:
react-query,SWRили аналоги. - Формы:
react-hook-formилиformik. - Производные значения: вычисляйте их на лету, не храните в состоянии.
Выводы и рекомендации
Если вы видите десятки useState в одном компоненте, это повод остановиться и подумать:
- Анализируйте состояние: что действительно является состоянием, а что можно вычислить или хранить в другом месте?
- Используйте правильные инструменты: не всё должно быть в
useState. - Тестируйте сценарии использования: как поведёт себя приложение при навигации, обновлении страницы, ошибках?
И помните: архитектура — это не про то, чтобы переместить код из компонента в отдельную папку. Это про то, чтобы правильно распределить ответственность.
А вы сталкивались с похожими ситуациями? Какие правила используете для определения, где должно жить состояние?
Источник: https://www.reddit.com/r/reactjs/comments/1vm69cb/counted_47_usestate_calls_in_one_route_while/