> Где хранить состояние в React (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: IT-One, Сбер, SmartWay

Стек: React

> Пример ответа

Короткий ответ

Состояние в React хранится в зависимости от его природы и области видимости: локальное - в useState или useReducer внутри компонента, глобальное - в контексте (React.createContext) или внешних стейт-менеджерах (Redux, Zustand, MobX). Для серверных данных - в React Query, SWR или RTK Query. Кэш URL (search params) - для фильтров и пагинации. Выбор диктуется trade-off между производительностью, сложностью синхронизации и масштабируемостью.

Подробное объяснение

Состояние в React делится на несколько категорий, каждая со своим местом хранения:

  1. Локальное состояние - данные, принадлежащие одному компоненту или его потомкам. Хранится в useState для простых значений, useReducer для сложной логики обновления. Пример: значение инпута, состояние модалки.

  2. Глобальное состояние - данные, доступные многим компонентам на разных уровнях дерева. Используется React.createContext + useContext для простых кейсов (тема, авторизация). Для сложных приложений - Redux (средствами Redux Toolkit), Zustand (минималистичный API), MobX (реактивное программирование). Выбор зависит от требований к traceability, middleware и размера команды.

  3. Серверное состояние - данные с бэкенда. Хранить в глобальном стейте - антипаттерн, так как требует ручной синхронизации с кэшем и инвалидации. Лучше использовать библиотеки: React Query (TanStack Query), SWR или RTK Query. Они автоматически управляют кэшированием, refetching, optimistic updates.

  4. URL/роутер - состояние, влияющее на навигацию: фильтры, пагинация, поисковые запросы. Хранится в search params (через useSearchParams из React Router или Next.js). Это позволяет делиться ссылками и поддерживать back/forward.

  5. Формы - сложное состояние с валидацией. Лучше выносить в библиотеки (React Hook Form, Formik), которые оптимизируют ререндеры и управляют полями.

  6. Кэш вычислений - мемоизированные значения через useMemo или библиотеки вроде Reselect для селекторов.

Правило: состояние должно быть как можно ближе к месту использования. Поднимать его вверх только при необходимости разделить между компонентами. Избегать хранения производных данных (вычисляемых из других состояний) - использовать useMemo.

На практике

  • Для простого приложения (до 5-10 компонентов) достаточно useState + useContext для темы/авторизации.
  • Для среднего приложения (с серверными данными) - React Query + локальный стейт в компонентах.
  • Для крупного enterprise (много бизнес-логики, middleware, сложные side effects) - Redux Toolkit (с RTK Query) или Zustand + React Query.
  • Для форм - React Hook Form, состояние формы не хранить в Redux.
  • Для фильтров/пагинации - URL search params, синхронизировать с React Query через keepPreviousData.
  • Избегать хранения в Redux того, что можно получить из контекста или URL.

Пример кода

JSX
// Локальное состояние
const [input, setInput] = useState('');
// Глобальное через контекст
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Main />
</ThemeContext.Provider>
);
}
// Серверное состояние через React Query
function Users() {
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
return isLoading ? <Spinner /> : <UserList users={data} />;
}
// URL состояние
function Filters() {
const [searchParams, setSearchParams] = useSearchParams();
const filter = searchParams.get('status') || 'all';
return (
<select value={filter} onChange={e => setSearchParams({ status: e.target.value })}>
<option value="all">All</option>
<option value="active">Active</option>
</select>
);
}

Как отвечать на собеседовании

Начни с классификации состояний, затем объясни критерии выбора: область видимости, источник данных (локальный/серверный), частота изменений, необходимость синхронизации между вкладками. Приведи конкретные примеры из практики, где выбор хранилища повлиял на производительность или поддерживаемость. Упомяни антипаттерны: хранение computed значений, дублирование состояния в нескольких местах, помещение всего в Redux. Будь готов обсудить trade-off между Context (ререндеры всех потребителей) и Redux (селекторы, мемоизация).

Что проверяет интервьюер

  • Понимание иерархии состояний и принципа single source of truth.
  • Умение выбирать инструмент под задачу, а не по привычке.
  • Знание проблем производительности (ререндеры, лишние вычисления).
  • Опыт работы с серверным состоянием и кэшированием.
  • Понимание разницы между UI-состоянием и бизнес-логикой.

Типичные ошибки

  • Хранение всего в Redux, включая временные UI-состояния (инпуты, модалки).
  • Использование Context для часто обновляемых данных (приводит к массовым ререндерам).
  • Хранение серверных данных в глобальном стейте без кэша и инвалидации.
  • Дублирование состояния (например, копия данных из Redux в локальный стейт).
  • Хранение computed значений (например, fullName = firstName + lastName) - использовать useMemo.
  • Игнорирование URL как источника состояния для фильтров.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью