React Query – why does it matter?

It avoids useEffect hell and handles: request state management, caching, refetching, retrying, “suspending” and error treatment; out of the box.

It helps with Asynchronous State management.

useEffect hell

You probably don’t need useEffect,…


This content originally appeared on DEV Community and was authored by Caio Borghi

It avoids useEffect hell and handles: request state management, caching, refetching, retrying, "suspending" and error treatment; out of the box.

It helps with Asynchronous State management.

useEffect hell

You probably don't need useEffect, specially for handling requests.

Code difference

bad

import { useState, useEffect } from 'react';

const UniverseList = () => {
  const [isLoading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [universes, setUniverses] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    const loadUniverses = async () => {
      try {
        setLoading(true);
        setError(null);

        const response = await fetch('/api/universes', {
          signal: controller.signal
        });

        if (!response.ok) {
          throw new Error(`Error: ${response.status} - ${await response.text()}`);
        }

        const jsonResponse = await response.json();
        setUniverses(jsonResponse.data || []);
      } catch (err) {
        if (err.name !== 'AbortError') {
          setError(err.message || 'Failed to load universes');
        }
      } finally {
        setLoading(false);
      }
    };

    loadUniverses();

    return () => {
      controller.abort();
    };
  }, []);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {universes.map(universe => (
        <div key={universe.id}>{universe.name}</div>
      ))}
    </div>
  );
};

export default UniverseList;

good

import { Suspense } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';

const FIVE_MINUTES = 5 * 60 * 1_000;
const TEN_MINUTES  = 10 * 60 * 1_000;

const fetchUniverses = async () => {
  const response = await fetch('/api/universes');

  if (!response.ok) {
    throw new Error(`Error: ${response.status} - ${await response.text()}`);
  }

  const jsonResponse = await response.json();
  return jsonResponse.data || [];
};

const UniverseListContent = () => {
  const { data: universes } = useSuspenseQuery({
    queryKey: ['universes'],
    queryFn: fetchUniverses,
    staleTime: FIVE_MINUTES,
    gcTime: TEN_MINUTES,
    retry: 3,
    retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
  });

  return (
    <div>
      {universes.map(universe => (
        <div key={universe.id}>{universe.name}</div>
      ))}
    </div>
  );
};

const ErrorFallback = ({ error, resetErrorBoundary }) => (
  <div role="alert">
    <p>Something went wrong:</p>
    <pre>{error.message}</pre>
    <button onClick={resetErrorBoundary}>Try again</button>
  </div>
);

const UniverseListWithSuspense = () => {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => window.location.reload()}
    >
      <Suspense fallback={<div>Loading universes...</div>}>
        <UniverseListContent />
      </Suspense>
    </ErrorBoundary>
  );
};

export default UniverseListWithSuspense;

Why bad?

Repeated code, required to manage async state, will spread as garden weeds as the project scales.

If you're not willing using React Query, at least create your own decoupled hooks and make sure to properly test them.

caching

React Query can cache your endpoints results and expire then after a configurable expiration time.

It also allows you to tie tags with queries and invalidate them on mutations.

refetching

As easy as calling a function, as it should be.

const UniverseListContent = () => {
  const { data: universes, refetch } = useSuspenseQuery({
    queryKey: ['universes'],
    queryFn: fetchUniverses,
    staleTime: FIVE_MINUTES,
    gcTime: TEN_MINUTES,
    retry: 
    retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
  });

  return (
    <div>
      <button onClick={refetch}>Reload All</button>
      {universes.map(universe => (
        <div key={universe.id}>{universe.name}</div>
      ))}
    </div>
  );
};


This content originally appeared on DEV Community and was authored by Caio Borghi


Print Share Comment Cite Upload Translate Updates
APA

Caio Borghi | Sciencx (2025-09-01T17:10:06+00:00) React Query – why does it matter?. Retrieved from https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/

MLA
" » React Query – why does it matter?." Caio Borghi | Sciencx - Monday September 1, 2025, https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/
HARVARD
Caio Borghi | Sciencx Monday September 1, 2025 » React Query – why does it matter?., viewed ,<https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/>
VANCOUVER
Caio Borghi | Sciencx - » React Query – why does it matter?. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/
CHICAGO
" » React Query – why does it matter?." Caio Borghi | Sciencx - Accessed . https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/
IEEE
" » React Query – why does it matter?." Caio Borghi | Sciencx [Online]. Available: https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/. [Accessed: ]
rf:citation
» React Query – why does it matter? | Caio Borghi | Sciencx | https://www.scien.cx/2025/09/01/react-query-why-does-it-matter/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.