В React-архитектуре каждое изменение ссылки на объект данных приводит к потенциальному ререндеру компонентов. В контексте серверного состояния это становится особенно заметно: даже если фактические данные не изменились, но изменился их контейнер (новая ссылка на объект, новый массив, пересобранный ответ), React будет считать это обновлением.
В TanStack Query это поведение проявляется при:
Без дополнительных оптимизаций компонент может перерендериваться даже тогда, когда используется лишь маленькая часть большого объекта ответа.
select в TanStack Queryselect — это функция трансформации данных, которая
позволяет преобразовать результат запроса до того, как он попадёт в
компонент.
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
select: (data) => data.map(user => user.name)
})
Ключевая особенность:
select выполняется после получения данных из кэшаselect становится новым значением
data в компонентеТаким образом, компонент подписывается не на весь объект ответа, а на его производную форму.
select уменьшает количество ререндеровОсновной механизм оптимизации основан на сравнении ссылок.
TanStack Query использует структурное разделение и мемоизацию, но:
select компонент получает полный объект данныхС select:
Пример:
const { data } = useQuery({
queryKey: ['user', id],
queryFn: fetchUser,
select: (user) => user.email
})
Если изменилось поле user.name, но email
остался тем же, компонент не обязан ререндериться.
selectselect не просто функция трансформации, она также
участвует в механизме мемоизации результата запроса.
TanStack Query повторно использует предыдущие вычисления при следующих условиях:
selectЭто означает, что при правильной архитектуре select
может вообще не вызываться повторно при ререндере компонента.
select фактически создаёт слой проекции данных:
Пример разделения:
const { data } = useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
select: (products) => ({
list: products.map(p => ({
id: p.id,
title: p.title
})),
count: products.length
})
})
Важно: даже если исходный массив products изменится,
компонент может не перерендериться, если результат select
не изменил ссылку.
select и стабильность
ссылокКлючевой момент оптимизации — управление referential equality.
TanStack Query применяет:
Однако select может как улучшить, так и ухудшить
ситуацию:
select: (data) => data.items
Если items стабилен — ререндеров меньше.
select: (data) => data.items.map(x => ({ ...x }))
Каждый вызов создаёт новые объекты → ссылки всегда новые → ререндеры неизбежны.
Без select часто приходится нормализовать данные в
глобальном сторе или на уровне компонента:
useMemoС select эти операции переносятся в слой запроса:
const { data: activeUsers } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
select: (users) => users.filter(u => u.active)
})
Это снижает нагрузку на React-компоненты и уменьшает количество вычислений при каждом рендере.
select и
queryKeyВажно учитывать, что select не влияет на кэширование
напрямую. Кэш в TanStack Query привязан к queryKey, а не к
результату select.
Это означает:
select не создают разные кэшиПример:
useQuery({
queryKey: ['user', id],
queryFn: fetchUser,
select: (user) => user.name
})
useQuery({
queryKey: ['user', id],
queryFn: fetchUser,
select: (user) => user.posts
})
Оба используют один кэш, но разные формы данных.
Каждый useQuery подписывается на изменения результата
после select. Это означает:
В сложных интерфейсах это критично:
selectselectconst { data } = useQuery({
queryKey: ['orders'],
queryFn: fetchOrders
})
Компонент получает:
selectconst { data } = useQuery({
queryKey: ['orders'],
queryFn: fetchOrders,
select: (orders) => orders.map(o => o.id)
})
Компонент получает:
Использование select влияет на три уровня:
1. React render layer
2. JavaScript execution
3. Memory footprint
Несмотря на преимущества, select имеет особенности:
Типичная ошибка:
select: (data) => ({
items: data.items.filter(x => x.visible)
})
Каждый вызов создаёт новый объект → стабильность теряется.
selectЭффективные подходы:
selectselect
как инструмент архитектуры данныхВ связке с TanStack Query select становится не просто
оптимизацией, а архитектурным инструментом:
Он превращает сырые серверные данные в строго ограниченные представления, адаптированные под конкретный UI-контекст, без необходимости создавать дополнительные слои состояния или мемоизации внутри компонентов.