Какая разница между серверными данными и state приложения?

🎯 Зачем спрашивают
  • Проверить, понимает ли кандидат разницу между слоями данных: где источник истины (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.
Список reducers в одном приложении
Список reducers в одном приложении
Когда смотришь на внушительный список reduces задаешься вопросом “неужели у приложения столько состояний?”. Нет, Redux использовался для того, чтобы получить серверные данные и обработать их состояние (ошибка, загрузка).
Для каждого редьюсера есть свои экшены, есть actions creators, селекторы. А главное — постоянное приходится повторять одну и ту же конструкцию.
const initialState = { error: false, fetch: false, data: ..., };
Типичный initial state для редьюсера
В основном эти редьюсеры существуют только для того, чтобы в них положили данные, которые были получены с сервера.
На просторах интернета можно встретить боль 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> ); }
Пример компонента, запрашивающего данные через react-query
Инструмент предоставляет данный функционал из коробки, никаких действий нам для этого делать не надо. И копировать одни и те же состояния тоже не нужно, как мы бы это делали с помощью Redux.
const initialState = { loading: false, error: null, todos: [], };
Типичный initial state для редьюсера
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: [], };
Типичный initial state для редьюсера
Что касается количества кода, то разработчиками 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
 
📚 Источники / ссылки