Skip to content

React Query

Paulo Gomes da Cruz Junior edited this page Apr 15, 2026 · 2 revisions

🔄 React Query Integration

Overview

React Query — now officially known as TanStack Query — is a powerful data-fetching and server state management library for React. It simplifies the process of fetching, caching, synchronizing, and updating remote data with minimal boilerplate.

“TanStack Query (formerly React Query) is often described as the missing data-fetching library for web applications. It makes fetching, caching, synchronizing, and updating server state a breeze.”
— TanStack Query Docs

TanStack Query helps you overcome the complexity of managing server state with useEffect, useState, and manual cache handling. In this project, production components do not call useQuery directly for backend data. Instead, they consume the shared hook layer in src/config/react-query/hooks.ts, which standardizes query keys, request contracts, and notification-aware error handling.

“React Query flips the process on its head by letting you declare what you need, and then handling how and when to fetch or refresh it behind the scenes.”
— Zero To Mastery Guide

📦 Installation

To install React Query and its supporting tools:

npm install @tanstack/react-query
npm install -D @tanstack/eslint-plugin-query

⚙️ Setup

Create a QueryClient and wrap your app with QueryClientProvider:

// src/main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { queryClientConfig } from '@/config/react-query/config';

const queryClient = new QueryClient(queryClientConfig);

<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>

⚙️ Configuration: config/react-query/config.ts

To centralize and customize React Query behavior across your app, we define a QueryClientConfig object. This includes default options for queries such as caching, stale time, garbage collection, and retry logic.

🧠 Retry Strategy

By default, we disable retries for failed queries. This is done via a reusable noRetry function, which can be replaced or extended for more sophisticated retry logic.

/**
 * A default retry function that disables retries for all queries.
 * This can be replaced with a more advanced strategy if needed.
 */
export const noRetry = (_failureCount: number, _error: unknown): boolean => {
  return false;
};

⚠️ If you forget to wrap your app in QueryClientProvider, none of the React Query hooks will work.

🧪 Project Usage Pattern

import { useSearchReportingUnitsQuery } from '@/config/react-query/hooks';

const WasteSearchTable = () => {
  const { data, isLoading, isError, refetch } = useSearchReportingUnitsQuery(
    {
      page: 0,
      size: 10,
      filters: {},
      sort: {},
    },
    {
      enabled: false,
      notificationTarget: 'waste-search',
      staleTime: Infinity,
    },
  );

  if (isLoading) return <p>Loading...</p>;
  if (isError) return <p>Error loading search results</p>;

  return <button onClick={() => refetch()}>{data?.content.length ?? 0} results</button>;
};

Shared Hook Rules

  • Production components use shared hooks from src/config/react-query/hooks.ts.
  • Query keys are defined in src/config/react-query/queryKeys.ts.
  • Query input types reuse the key-factory parameter types.
  • notificationTarget is used when a query failure should emit a scoped notification event.

🧪 Example: Advanced Retry Strategy

If you want to implement conditional retries based on error type or HTTP status codes, here's a more advanced example:

import { isAxiosError } from 'axios';
import { QueryClientConfig } from '@tanstack/react-query';
import { THREE_HOURS } from '@/config/react-query/TimeUnits';

const HTTP_STATUS_TO_NOT_RETRY = [400, 401, 403, 404];
const MAX_RETRIES = 3;

/**
 * Retry function that:
 * - Limits retries to MAX_RETRIES
 * - Skips retry for specific HTTP status codes
 */
const conditionalRetry = (failureCount: number, error: unknown): boolean => {
  if (failureCount > MAX_RETRIES) return false;

  if (
    isAxiosError(error) &&
    HTTP_STATUS_TO_NOT_RETRY.includes(error.response?.status ?? 0)
  ) {
    return false;
  }

  return true;
};

You can swap noRetry with conditionalRetry in your config if needed.

✅ ESLint Support

To catch common mistakes and enforce best practices, enable the React Query ESLint plugin:

// eslint.config.json
{
  plugins: {
      '@tanstack/query': tanstackQuery,
  }
}

Typescript Support

React Query is built with TypeScript and provides first-class type definitions. You can strongly type your query functions and results. For it to work seamlessly, we need to add the types to our tsconfig.app.json:

{
  "compilerOptions": {
    "types": ["@tanstack/react-query"]
  }
}

🧠 Why Use React Query?

  • ✅ Automatic caching and background updates
  • 🔁 Retry logic and stale data handling
  • 🚫 No need for global state or Redux for server data
  • 🧼 Cleaner, declarative data-fetching logic

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally