🎯 Зачем спрашивают
- Проверить, понимает ли кандидат разницу между слоями данных: где источник истины (server state), а где локальное состояние (UI state).
- Оценить, умеет ли он выбирать подходящие инструменты: cache manager (react-query, SWR, RTK Query) vs state manager (Redux, Zustand, MobX).
- Понять, способен ли кандидат увидеть риски смешения слоёв.
- Проверить знание современных практик: cache invalidation, optimistic updates, синхронизация данных между компонентами.
- Убедиться, что кандидат понимает компромисс между простотой и масштабируемостью (локальный стейт vs глобальный стейт vs кэш).
📝 Ответ
Коротко:
Серверные данные — данные, получаемые приложение по сети. Для работы с данными и оркестрации их состояний (состояние загрузки, состояние ошибки, инвалидация) нужны cache managers (
react-query, swr, RTK Query)State приложения — локальное состояние (UI-состояния, временные данные, выбранные фильтры). Для состояния приложения используются state managers (
Redux, Zustand, MobX).Да, обработку серверных данных можно вынести в state managers, но…
Это может быть черевато:
- дублирование кода
- смешением разных слоев приложения (логики и data-fetching)
Подробнее:
Зачастую в приложениях для работы с серверными данными используется state manager.
Далее разберем пример одного микрофронтенд приложения, где использовался Redux.
Когда смотришь на внушительный список reduces задаешься вопросом “неужели у приложения столько состояний?”. Нет, Redux использовался для того, чтобы получить серверные данные и обработать их состояние (ошибка, загрузка).
Для каждого редьюсера есть свои экшены, есть actions creators, селекторы. А главное — постоянное приходится повторять одну и ту же конструкцию.
const initialState = { error: false, fetch: false, data: ..., };
В основном эти редьюсеры существуют только для того, чтобы в них положили данные, которые были получены с сервера.
На просторах интернета можно встретить боль frontend комьюнити — проект обрастает огромным количество «обслуживающего» Redux-кода. Зачастую в этом винят саму библиотеку, но это не ее вина. Концепт библиотеки совсем в другом.
Разработчики Redux хотели создать state manager для упрощения работы приложения с состояниями. И сделать эту работу предсказуемой.
Кэш данных
Server state — данные, хранящиеся на бэкенде.
Client cache — клиентская копия данных. получаемых по сети. Синхронизируется с сервером.
Разберем на примере библиотекиreact-query, но есть и аналоги
Обратите внимание на
useQuery. В данный хук мы передаем специально описанный запрос, требующий нужные нам данные. Данный хук сразу возвращает все состояния данных: - data — сами данные;
- isLoading — флаг, показывающий, загружены ли данные;
- isError — ошибку, если она есть.
const GET_DOGS_URL = 'https://some-cool-api/dogs' function Dogs({ onDogSelected }) { const { isLoading, isError, data } = useQuery(GET_DOGS_URL); if (isLoading) return 'Loading...'; if (isError) return 'Error!'; return ( <select name='dog' onChange={onDogSelected}> {data.dogs.map((dog) => ( <option key={dog.id} value={dog.breed}> {dog.breed} </option> ))} </select> ); }
Инструмент предоставляет данный функционал из коробки, никаких действий нам для этого делать не надо. И копировать одни и те же состояния тоже не нужно, как мы бы это делали с помощью Redux.
const initialState = { loading: false, error: null, todos: [], };
RQ получает данные и хранит их в кэше. Кэш — это глобальный нормализованный (плоский) объект.
{ users: [...], todos: [...], helpMePlease: ..., }
Как это работает? Один компонент запрашивает данные, другой — берет эти данные уже из кэша. Первый запрос за данными кладет данные именно в кэш. Также кэш обновляется, когда мы отправляем запрос на обновление данных на сервер.
Вот схема, как бы это работало, если бы три компонента одновременно запрашивали todo-элементы.
# 1 Первый компонент, который замонтировался в DOM, инициализировал бы запрос.
(на схеме Component 1)
# 2 Данные от сервера будут положены в кэш.
(Request-Data)
# 3 Остальные компоненты получают данные уже из кэша (если не указан параметр принудительного запроса к API, с обновление кэша).
(Component 2, Component 3)
Какие проблемы решает данный подход:
- Избежание дублирования запросов одних и тех же данных.
- Оптимистичные обновления, чтобы сделать пользовательский интерфейс более быстрым.
- Отслеживание состояния загрузки или состояния ошибки для отображения соответствующих элементов пользовательского интерфейса (loading, error, data).
- Управление временем жизни кэша при взаимодействии пользователя с пользовательским интерфейсом.
- Props drilling.
Вернемся к проблеме большого количество кода в Redux. Концепция библиотеки заключается совсем в другом, ее задача — управление состоянием, а не кэширование данных. Именно поэтому постоянно пишутся подобные конструкции, а сама библиотека на уровне API не предоставляет готовых решений.
const initialState = { loading: false, error: null, todos: [], };
Что касается количества кода, то разработчиками Redux и не планировалось, что его будет много в приложениях, так как в среднем у приложений не так уж и много состояний, которые надо менеджерить.
В основном redux store пухнет, потому что в него кладут данные, полученные от сервера и используемые несколькими компонентами одновременно.
Redux пытался решить проблему с помощью библиотеки redux-toolkit. Однако корень проблемы был совсем в другом, поэтому пользователям по-прежнему приходится писать значительные объемы логики редьюсера для управления состоянием загрузки и кэшированными данными.
А позже в Redux Toolkit появился еще и
RTK Query. По функционалу он похож на react-query.
⚖️ Компромиссы
Использование кэш-менеджеров
✅ Плюсы | ❌ Минусы |
Упрощает логику обработки загрузки/ошибок | Дополнительная зависимость |
Меньше boilerplate | Иногда нужно интегрировать с глобальным стейтом (например, фильтры + данные из API) |
ㅤ | Сложно интегрировать в приложение, где много фронтовых состояний. Например, веб-игры |
🔎 Встречные вопросы
- Когда стоит держать серверные данные в state managet?
- Чем RTK Query отличается от react-query?
- Как решать конфликт между UI state и server cache?
- Как правильно делать optimistic updates?
- Что такое инвалидация кэша?
🚩 Красные флаги
- Все данные всегда надо класть в Redux.
- Нет разницы между серверными данными и state — всё одно и то же.
🛠 Практика
TODO