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

Архітектура та Best Practices

Ми вивчили API. Тепер поговоримо про те, як не перетворити проект на смітник. TanStack Query дає багато свободи, але "з великою силою приходить велика відповідальність".

Архітектура та Best Practices

Ми вивчили API. Тепер поговоримо про те, як не перетворити проект на смітник. TanStack Query дає багато свободи, але "з великою силою приходить велика відповідальність".

Ось правила, які використовують професійні команди.

1. Кастомні Хуки — Це Закон

Ніколи не викликайте useQuery прямо в UI компонентах (якщо це не прототип на коліні).

Чому?

  1. Приховування деталей реалізації: Компонент не має знати про queryKey або staleTime.
  2. Повторне використання: Якщо вам треба отримати користувача в Header і в Sidebar, ви просто викликаєте useUser().
  3. Легкий рефакторинг: Змінився ключ? Змінилась логіка? Ви правите це в одному місці.
// ❌ Погано: Компонент знає занадто багато
function UserProfile() {
  const { data } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
  // ...
}

// ✅ Добре: Компонент просто просить дані
function UserProfile() {
  const { data } = useUser();
  // ...
}

// hooks/useUser.ts
export const useUser = () => {
  return useQuery({
    queryKey: userKeys.me(),
    queryFn: api.getUser,
    staleTime: 1000 * 60 * 30, // Ми вирішили, що юзер не змінюється часто
  });
};

2. Фабрики Ключів (Query Key Factories)

Ми згадували це в розділі 3, але це варто повторити. Хаос у ключах — головна причина багів інвалідації.

Використовуйте бібліотеку @lukemorales/query-key-factory або пишіть об'єкти вручну.

const todoKeys = {
  all: ['todos'] as const,
  lists: () => [...todoKeys.all, 'list'] as const,
  list: (filters: string) => [...todoKeys.lists(), { filters }] as const,
  details: () => [...todoKeys.all, 'detail'] as const,
  detail: (id: number) => [...todoKeys.details(), id] as const,
};

3. Розділення Шарів (Layered Architecture)

Чітко розділяйте відповідальність:

  1. API Layer (api/todos.ts): Чисті функції, які роблять fetch / axios і повертають Promise. Ніякого React.
  2. Query Layer (hooks/useTodos.ts): Кастомні хуки, які використовують API функції та додають логіку кешування (useQuery).
  3. UI Layer (components/TodoList.tsx): Використовує хуки та рендерить JSX.

4. Глобальна Обробка Помилок

Замість того, щоб перевіряти isError у кожному компоненті, використовуйте QueryCache global callbacks для тостів, та Error Boundaries для UI.

// queryClient.ts
const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error) => {
      toast.error(`Something went wrong: ${error.message}`);
    },
  }),
});

В компонентах:

// Якщо ви використовуєте useSuspenseQuery, помилка "спливе" до найближчого ErrorBoundary
function App() {
  return (
    <ErrorBoundary fallback={<div>Something broke!</div>}>
      <Suspense fallback={<div>Loading...</div>}>
        <UserProfile />
      </Suspense>
    </ErrorBoundary>
  );
}

5. Тестування

Не тестуйте TanStack Query. Він протестований авторами. Тестуйте ваші хуки та інтеграцію.

Для тестів створіть окремий queryClient з вимкненими retry (щоб тести не чекали вічність при помилках).

const createTestQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: {
      retry: false, // Важливо для тестів!
    },
  },
});

test('renders todos', async () => {
  render(
    <QueryClientProvider client={createTestQueryClient()}>
      <TodoList />
    </QueryClientProvider>
  );
  // ...
});

Висновок

TanStack Query — це потужний інструмент, який при правильному використанні робить ваш код чистішим, а додаток — швидшим.

Чек-лист професіонала:

  • Всі запити в кастомних хуках.
  • Ключі зібрані в Key Factory.
  • staleTime налаштовано глобально та перевизначено локально де треба.
  • Використовуються DevTools.
  • Мутації автоматично інвалідують пов'язані запити.

Вітаємо! Ви пройшли курс майстерності TanStack Query. Тепер йдіть і видаліть весь цей useEffect код зі своїх проектів! 🚀

Copyright © 2026