TanStack Query: Майстерність Керування Станом Сервера

Глибоке Занурення в Продуктивність

TanStack Query надзвичайно швидкий "з коробки". Але якщо ви рендерите тисячі елементів або маєте складні дані, варто розуміти, як він працює під капотом.

Глибоке Занурення в Продуктивність

TanStack Query надзвичайно швидкий "з коробки". Але якщо ви рендерите тисячі елементів або маєте складні дані, варто розуміти, як він працює під капотом.

Tracked Queries (Відстежування Властивостей)

Починаючи з v4, опція notifyOnChangeProps за замовчуванням встановлена в 'tracked'. Це магія.

Як це працює?

Query створює Proxy навколо результату. Він "слухає", до яких полів ви звертаєтесь під час рендеру.

function User() {
  const { data } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
  
  // Ми читаємо ТІЛЬКИ data.
  // Ми НЕ читаємо isFetching, isError, error.
  return <div>{data.name}</div>;
}

Якщо в фоні зміниться isFetching (наприклад, почався refetch), цей компонент НЕ перерендериться, тому що він не використовує це поле.

Це колосальна оптимізація. Раніше кожен компонент рендерився двічі на кожен запит (start -> success). Тепер — тільки якщо змінились дані.

Тому деструктуризація (const { data } = ...) — це не просто стиль, це спосіб підказати Query, що нам потрібно.

Structural Sharing (Структурне Спільне Використання)

Уявіть, що ви отримуєте JSON:

{ "id": 1, "todos": [{ "text": "A" }, { "text": "B" }] }

Через хвилину ви робите refetch, і приходить той самий JSON. oldData === newData буде false, тому що це новий об'єкт з мережі. React зробить ререндер.

TanStack Query використовує техніку Structural Sharing: Він порівнює старий і новий об'єкт глибоко.

  1. Якщо вони ідентичні — він залишає посилання на старий об'єкт.
  2. Якщо змінилось тільки одне поле — він створює новий об'єкт, але копіює посилання на незмінені вкладені об'єкти.

Результат: useQuery повертає стабільне посилання. useEffect і useMemo, які залежать від data, не перезапускаються.

Оптимізація через select

Ми вже говорили про select, але це головний інструмент для performance tuning.

const { data: todoCount } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  select: (todos) => todos.length, // Повертає число
});

Якщо в список додали елемент — компонент оновиться. Але якщо в одному з todo змінився текст (а довжина масиву та сама) — компонент не оновиться! Тому що select повернув те саме число (якщо ви використовуєте useCallback для select, або якщо це примітив).

Persisters (Збереження Кешу)

Ви можете зберігати кеш в localStorage або AsyncStorage (React Native), щоб при перезавантаженні сторінки дані з'являлися миттєво (навіть без мережі).

Це називається Offline Support.

import { persistQueryClient } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';

const persister = createSyncStoragePersister({
  storage: window.localStorage,
});

persistQueryClient({
  queryClient,
  persister,
  maxAge: 1000 * 60 * 60 * 24, // Зберігати добу
});

Висновок по Продуктивності

  1. Не бійтеся робити багато useQuery для тих самих даних. Завдяки дедуплікації це дешево.
  2. Використовуйте select для підписок на частини даних.
  3. Довіртеся Tracked Queries — вони зроблять більшість роботи за вас.
  4. Слідкуйте за стабільністю посилань в queryFn та options (виносьте їх за межі компонента).
Copyright © 2026