Dev Highlights

Доки по разработке

This project is maintained by teniryte

11. Devtools и диагностика производительности

Devtools помогают быстро увидеть всё, что происходит в кеше: статусы запросов, очереди мутаций, пересоздания ключей и частоту пересчётов селекторов. В v5 панель вынесена в @tanstack/react-query-devtools, умеет работать как встроенный виджет или отдельная вкладка и поддерживает тёмную тему, собственные кнопки и динамический импорт.

Быстрый чеклист перед расследованием

Подключение

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

<QueryClientProvider client={queryClient}>
  <App />
  {import.meta.env.DEV && (
    <ReactQueryDevtools
      initialIsOpen={window.location.hash === '#rq'}
      position="bottom"
      buttonPosition="bottom-right"
      panelProps={{ style: { maxHeight: '45vh', boxShadow: '0 -8px 32px rgba(0,0,0,.35)' } }}
      buttonProps={{ 'aria-label': 'Toggle React Query Devtools' }}
      closeButtonProps={{ style: { opacity: 0.6 } }}
    />
  )}
</QueryClientProvider>

Динамический импорт

const Devtools = import.meta.env.DEV
  ? lazy(() => import('@tanstack/react-query-devtools').then(m => ({ default: m.ReactQueryDevtools })))
  : () => null;

Используйте для уменьшения основного бандла; Suspense здесь подходит, потому что Devtools не критичны.

Отдельная панель или вкладка

import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';

export function QueryInspector() {
  const queryClient = useQueryClient();
  return import.meta.env.DEV ? (
    <ReactQueryDevtoolsPanel
      queryClient={queryClient}
      style=
      onClose={() => console.log('Devtools hidden')}
    />
  ) : null;
}

Такой компонент можно встроить в сторибуки, дизайн-системы или вынести в отдельное окно Electron.

Что показывает Devtools

Полезные настройки панели

Диагностика рефетчей

  1. Включите showStale. Если запрос мгновенно становится stale, значит staleTime = 0 и каждый фокус окна будет вызывать refetch.
  2. Если ключ выглядит как [object Object], значит передаётся немемуизированный объект. В Devtools это легко увидеть, включив Show query keys.
  3. Нажмите на строку запроса, вкладка Observers покажет options. Ищите параметры по умолчанию, которые не переопределены (например, refetchOnWindowFocus: true).
  4. Посмотрите вкладку Events — там фиксируются observerAdded, observerRemoved, queryUpdated, queryGC. Если observerAdded срабатывает чаще, чем ожидалось, значит компоненты пересоздаются.
  5. Используйте queryClient.getQueryCache().find({ queryKey })?.observers.length в runtime-логах, чтобы сверять с Devtools.

Логи и события

const unsubscribe = queryClient.getQueryCache().subscribe(event => {
  if (event?.type === 'queryObserverResultUpdated' && event.query.state.fetchStatus === 'fetching') {
    performance.mark(`rq:${event.query.queryHash}:fetching`);
  }
  if (event?.type === 'queryRemoved') {
    console.debug('[RQ] GC', event.query.queryKey);
  }
});

queryClient.getMutationCache().subscribe(event => {
  if (event?.type === 'mutationAdded') {
    console.info('[RQ] Mutation enqueued', event.mutation.options.mutationKey);
  }
});

Профилирование ререндеров

Стратегии оптимизации

Дебаг мутаций

Производительность больших таблиц

Настройка Devtools в Storybook

addDecorator(Story => (
  <QueryClientProvider client={queryClient}>
    <Story />
    <ReactQueryDevtools initialIsOpen={true} position="top" />
  </QueryClientProvider>
));

Интеграция с профайлерами

Типовые сценарии расследования

FAQ: что смотреть в первую очередь

Бонус: эмуляция плохой сети

if (import.meta.env.DEV) {
  queryClient.setDefaultOptions({
    queries: {
      retryDelay: attempt => Math.min(1000 * attempt, 5000),
      networkMode: 'offlineFirst',
    },
  });
}

Совместите с Chrome Network Throttling и наблюдайте в Devtools, как растёт failureCount. Это помогает подобрать реалистичные retry.