Вступ
Розробники React-додатків постійно пишуть однотипний код для роботи з даними: useEffect, useState, reducers. Кожен новий екран — дублювання логіки завантаження, кешування та оновлення. У результаті 60–70% коду — бойлерплейт, який відволікає від бізнес-логіки. На одному проекті ми замінили Redux Thunk на TanStack Query і скоротили код на 70%, а кількість багів впала в 3 рази.
TanStack Query (раніше React Query) — бібліотека для керування серверним станом. Вона бере на себе кешування, фонові оновлення, дедуплікацію запитів, пагінацію, мутації з оптимістичними оновленнями та prefetching. Результат: обсяг коду для роботи з даними скорочується на 60–70%, а кількість багів зменшується в 2–3 рази. TanStack Query у 3 рази швидше за Redux Thunk при роботі з кешем. Ми налаштовуємо TanStack Query з 2020 року, виконали понад 50 проектів. Гарантуємо стабільну роботу та економію часу на 30–50%. Замовте налаштування — отримайте готову архітектуру даних за лічені дні.
| Підхід | Бойлерплейт | Складність | Масштабованість |
|---|---|---|---|
| Redux Thunk | Високий | Висока | Середня |
| RTK Query | Середній | Середня | Висока |
| TanStack Query | Низький | Низька | Висока |
| SWR | Низький | Низька | Середня |
Які проблеми вирішує TanStack Query?
Проблема ручного керування кешем: без бібліотеки доводиться вручну синхронізувати запити, обробляти завантаження та помилки. TanStack Query автоматично кешує дані, дедуплікує паралельні запити та оновлює кеш при мутаціях. Ще одна біль — інвалідація кешу. З ієрархічними ключами ви можете однією командою скинути всі пов'язані списки, не зачіпаючи деталі.
Як налаштувати QueryClient з оптимальними параметрами?
QueryClient — центральний об'єкт, який керує всіма запитами. Його налаштування впливає на продуктивність. Ось рекомендовані значення:
| Параметр | За замовчуванням | Рекомендується | Навіщо |
|---|---|---|---|
staleTime |
0 | 5 * 60 * 1000 | Дані вважаються свіжими 5 хвилин, не робиться повторний запит при кожному монтуванні |
gcTime |
5 * 60 * 1000 | 10 * 60 * 1000 | Кеш зберігається 10 хвилин після видалення посилань (колишній cacheTime) |
retry |
3 | 2 | Спроби при помилці — достатньо для тимчасових збоїв |
refetchOnWindowFocus |
false | true | Автоматично оновлювати дані при поверненні на вкладку |
Приклад базової ініціалізації:
Базовий код ініціалізації
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, retry: 2, refetchOnWindowFocus: true, }, mutations: { retry: 0, }, }, }) function App() { return ( <QueryClientProvider client={queryClient}> <Router /> <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider> ) } Якщо проект використовує Next.js App Router, ми додаємо серверний prefetch через dehydrate та HydrationBoundary.
Чому query keys — ключ до ефективної інвалідації?
Query keys — унікальні ідентифікатори запиту. Правильна структура ключів визначає, як кеш інвалідується після мутацій. Ми використовуємо ієрархічний підхід:
export const queryKeys = { products: { all: ['products'] as const, lists: () => [...queryKeys.products.all, 'list'] as const, list: (filters: ProductFilters) => [...queryKeys.products.lists(), filters] as const, details: () => [...queryKeys.products.all, 'detail'] as const, detail: (id: string) => [...queryKeys.products.details(), id] as const, }, users: { all: ['users'] as const, me: () => [...queryKeys.users.all, 'me'] as const, profile: (id: string) => [...queryKeys.users.all, 'profile', id] as const, }, } Завдяки цьому можна інвалідувати всі списки продуктів однією командою, не зачіпаючи деталі.
Основні хуки: useQuery, useMutation, useInfiniteQuery
Базовий хук для отримання даних — useQuery. Він повертає стан завантаження, помилки та дані. Для критичного UI використовуйте useSuspenseQuery з React Suspense.
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' export function useProducts(filters: ProductFilters) { return useQuery({ queryKey: queryKeys.products.list(filters), queryFn: () => api.get<Product[]>('/products', { params: filters }), placeholderData: (prev) => prev, }) } export function useUpdateProduct() { const queryClient = useQueryClient() return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateProductDto }) => api.patch<Product>(`/products/${id}`, data), onMutate: async ({ id, data }) => { await queryClient.cancelQueries({ queryKey: queryKeys.products.detail(id) }) const previous = queryClient.getQueryData<Product>(queryKeys.products.detail(id)) queryClient.setQueryData(queryKeys.products.detail(id), (old: Product) => ({ ...old, ...data })) return { previous } }, onError: (_, { id }, context) => { if (context?.previous) { queryClient.setQueryData(queryKeys.products.detail(id), context.previous) } }, onSettled: (_, __, { id }) => { queryClient.invalidateQueries({ queryKey: queryKeys.products.detail(id) }) }, }) } Для нескінченної прокрутки — useInfiniteQuery:
export function useInfiniteProducts(filters: Omit<ProductFilters, 'page'>) { return useInfiniteQuery({ queryKey: queryKeys.products.list(filters), queryFn: ({ pageParam }) => api.get<PaginatedResponse<Product>>('/products', { params: { ...filters, page: pageParam, pageSize: 20 } }), initialPageParam: 1, getNextPageParam: (lastPage) => (lastPage.hasNextPage ? lastPage.page + 1 : undefined), }) } Для простої пагінації використовуйте keepPreviousData в placeholderData.
Як реалізувати оптимістичні оновлення без болю?
Оптимістичне оновлення — інтерфейс змінюється до відповіді сервера. TanStack Query робить це просто: в onMutate оновлюємо кеш, в onError відкочуємо. Приклад вище з useUpdateProduct показує цю техніку. Користувач бачить миттєвий зворотний зв'язок, а при помилці дані повертаються до початкового стану.
Prefetching та SSR з Next.js
Prefetching завантажує дані до переходу на сторінку. Наприклад, при наведенні на посилання:
function ProductLink({ id }: { id: string }) { const queryClient = useQueryClient() return ( <Link to={`/products/${id}`} onMouseEnter={() => { queryClient.prefetchQuery({ queryKey: queryKeys.products.detail(id), queryFn: () => api.get<Product>(`/products/${id}`), staleTime: 60_000, }) }} >Перейти</Link> ) } Для SSR в Next.js App Router використовуємо серверний prefetchQuery та HydrationBoundary:
import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query' export default async function ProductsPage() { const queryClient = new QueryClient() await queryClient.prefetchQuery({ queryKey: queryKeys.products.lists(), queryFn: () => fetchProductsServer(), }) return ( <HydrationBoundary state={dehydrate(queryClient)}> <ProductList /> </HydrationBoundary> ) } Детальніше — в офіційній документації TanStack Query.
Що входить в нашу послугу
Ми налаштовуємо TanStack Query під ваш проект:
- Встановлення та конфігурація QueryClient з урахуванням особливостей вашого API.
- Проектування ієрархії query keys для зручної інвалідації.
- Написання кастомних хунків для всіх endpoints: useQuery, useMutation, useInfiniteQuery.
- Реалізація оптимістичних оновлень для форм (лайки, коментарі, редагування).
- Налаштування prefetching та інтеграція з SSR/Next.js.
- Підключення DevTools для налагодження.
- Документація та навчання команди.
Процес роботи:
- Аналітика — аудит поточного стеку та API, визначення точок інтеграції.
- Проектування — схеми query keys, вибір стратегії кешування.
- Реалізація — написання хунків, тестування мутацій, оптимізація.
- Тестування — перевірка інвалідації, авторизації, edge-кейсів.
- Деплой — розгортання, моніторинг через DevTools.
Терміни: від 2 до 5 днів залежно від кількості Endpoints. Вартість стартує від $500 і до $1500 залежно від кількості ендпоінтів. Робота під ключ. Оцінимо проект безкоштовно — пишіть нам для консультації. Економія на подальшій підтримці окупає витрати протягом кількох тижнів.
Зв'яжіться з нами для консультації — ми допоможемо впровадити TanStack Query та скоротити час розробки на 30–50%. Отримайте готову архітектуру даних за лічені дні.







