Поведение подписки на изменения в TanStack Query напрямую влияет на
количество ререндеров компонентов и общую производительность приложения.
Одним из ключевых механизмов тонкой настройки является
notifyOnChangeProps, позволяющий контролировать, какие
именно изменения данных должны приводить к обновлению компонента.
По умолчанию useQuery подписывается на все изменения
результата запроса. Это означает, что любое изменение состояния
query-объекта — будь то обновление data,
status, isFetching, error или
даже метаданных — может вызвать ререндер компонента.
Такая модель проста, но в крупных приложениях приводит к избыточным обновлениям UI. Особенно это заметно в случаях, когда компонент использует только небольшую часть данных, но подписан на весь объект результата.
Типичный пример:
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
})
Даже если компонент использует только data, он всё равно
будет перерендериваться при изменении других полей результата.
notifyOnChangeProps позволяет ограничить список свойств,
изменение которых вызывает ререндер компонента.
Возможные значения:
'all' — поведение по умолчанию, подписка на все
измененияБазовое использование:
const query = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
notifyOnChangeProps: ['data'],
})
В этом случае компонент будет перерендериваться только при изменении
data, игнорируя изменения остальных полей результата.
При использовании массива строк TanStack Query сравнивает изменения только указанных полей. Если изменяется поле, не входящее в список, ререндер не происходит.
Пример:
const query = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
notifyOnChangeProps: ['data', 'error'],
})
Теперь компонент реагирует только на:
Изменения isFetching, status,
isLoading игнорируются с точки зрения ререндера.
Важно учитывать, что сама подписка на query остаётся полной — фильтруется только уведомление о необходимости рендера.
Функциональный вариант notifyOnChangeProps позволяет
динамически определять зависимости на основе предыдущих и текущих
значений.
const query = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
notifyOnChangeProps: (props, prevProps) => {
if (props.data !== prevProps.data) {
return ['data']
}
return []
},
})
Здесь компонент можно заставить реагировать только на конкретные изменения, игнорируя остальные.
Такая форма используется редко, поскольку требует аккуратного управления сравнением и легко приводит к ошибкам при сложной структуре данных.
Использование notifyOnChangeProps часто становится
инструментом оптимизации в компонентах с высокой частотой обновлений или
сложной вложенной структурой UI.
Типичный сценарий:
isFetching при
фоновом рефетчеdataПример:
function UsersList() {
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
notifyOnChangeProps: ['data'],
})
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
Здесь изменение фонового состояния запроса не вызывает лишних обновлений списка.
Использование notifyOnChangeProps требует понимания
внутренних механизмов TanStack Query.
Ключевые ограничения:
Особенно важно учитывать, что игнорирование isFetching
или status может скрыть важные состояния загрузки.
В современных версиях TanStack Query предпочтение часто отдаётся
select, который позволяет не только контролировать
ререндер, но и трансформировать данные.
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
select: (data) => data.filter(user => user.active),
})
В таких случаях notifyOnChangeProps становится менее
востребованным, так как select уже сокращает поверхность
изменений.
Однако при необходимости тонкой настройки ререндеров они могут использоваться совместно.
Во время повторных запросов TanStack Query может обновлять несколько полей состояния одновременно:
isFetchingstatusdataUpdatedAtfetchStatusПри стандартной подписке компонент будет реагировать на каждое из
этих изменений. С notifyOnChangeProps: ['data'] ререндер
произойдёт только при фактическом изменении data, но не при
изменении статуса запроса.
Это особенно важно при фоновых обновлениях данных, когда UI не должен визуально изменяться.
Оптимизация через notifyOnChangeProps имеет смысл в
следующих случаях:
В небольших приложениях эффект может быть незаметен, а усложнение логики — избыточным.
Часто notifyOnChangeProps используется вместе с
React.memo, создавая двойной слой защиты от лишних
ререндеров:
const UsersList = React.memo(() => {
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
notifyOnChangeProps: ['data'],
})
return ...
})
В этом случае:
notifyOnChangeProps ограничивает триггеры
обновленияReact.memo предотвращает ререндер от родителяТакая комбинация усиливает контроль над render pipeline.
В development-режиме React и TanStack Query могут добавлять
дополнительные проверки и поведение, связанное с Strict Mode. Это иногда
создаёт впечатление, что notifyOnChangeProps работает менее
предсказуемо из-за двойных ререндеров.
В production эти эффекты исчезают, и поведение становится более стабильным.
До появления селекторов и развитой системы оптимизаций использовались:
useMemo и
useSelectornotifyOnChangeProps стал промежуточным решением между
грубой подпиской и полной селекцией данных, позволяя управлять
ререндером без изменения структуры данных.
Сегодня его роль более узкая, но в низкоуровневой оптимизации он остаётся актуальным инструментом контроля поведения подписок.