Error Handling and Retry Logic with TanStack Query

Error handling and retry logic are critical features for creating robust applications that fetch data from unreliable or slow APIs. TanStack Query simplifies managing errors and retries, providing fine-grained control to handle different failure scenarios.


This tutorial will guide you through:

  • Handling errors in queries and mutations.
  • Configuring retry logic.
  • Providing user-friendly feedback for errors.


Step 1: Setting Up TanStack Query

Install TanStack Query

npm install @tanstack/react-query

npm install @tanstack/react-query-devtools

Setup Query Client

Wrap your application with QueryClientProvider to enable query management.


App.js

import React from 'react';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

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

import { UsersList } from './UsersList';


const queryClient = new QueryClient();


function App() {

  return (

    <QueryClientProvider client={queryClient}>

      <h1>Error Handling with TanStack Query</h1>

      <UsersList />

      <ReactQueryDevtools initialIsOpen={false} />

    </QueryClientProvider>

  );

}


export default App;


Step 2: Basic Error Handling

TanStack Query provides an isError flag and an error object to detect and display errors.

Example: Fetching Users with Error Handling

UsersList.js

import React from 'react';

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


const fetchUsers = async () => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users-invalid-endpoint');

  if (!response.ok) {

    throw new Error('Failed to fetch users');

  }

  return response.json();

};


export function UsersList() {

  const { data, isLoading, isError, error } = useQuery(['users'], fetchUsers);


  if (isLoading) return <p>Loading users...</p>;


  if (isError) {

    return <p>Error: {error.message}</p>;

  }


  return (

    <ul>

      {data.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}


Explanation:

If the fetch request fails, an error is thrown, and the isError flag becomes true.

The error.message displays the reason for failure.


Step 3: Configuring Retry Logic

By default, TanStack Query retries failed queries 3 times with exponential backoff. You can customize this behavior.


Custom Retry Configuration

Use the retry and retryDelay options to define retry logic.


Example: Retry Logic

export function UsersList() {

  const { data, isLoading, isError, error } = useQuery(['users'], fetchUsers, {

    retry: 2, // Retry up to 2 times

    retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 3000), // Exponential backoff

  });


  if (isLoading) return <p>Loading users...</p>;

  if (isError) return <p>Error: {error.message}</p>;


  return (

    <ul>

      {data.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}


Explanation:

retry: 2 limits retries to two attempts.

retryDelay defines the delay between retries using exponential backoff.


Step 4: Retry on Specific Errors

You can customize retry logic to only retry specific errors.


Example: Retry for Certain Status Codes

const fetchUsers = async () => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users-invalid-endpoint');

  if (!response.ok) {

    if (response.status === 404) {

      throw new Error('Not Found');

    }

    if (response.status >= 500) {

      throw new Error('Server Error');

    }

  }

  return response.json();

};


export function UsersList() {

  const { data, isLoading, isError, error } = useQuery(['users'], fetchUsers, {

    retry: (failureCount, error) => error.message === 'Server Error', // Retry only on server errors

  });


  if (isLoading) return <p>Loading users...</p>;

  if (isError) return <p>Error: {error.message}</p>;


  return (

    <ul>

      {data.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}


Step 5: Handling Errors in Mutations

Handle errors during create, update, or delete operations with useMutation.


Example: Adding a User with Error Handling

AddUser.js

import React, { useState } from 'react';

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


const addUser = async (newUser) => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users', {

    method: 'POST',

    headers: { 'Content-Type': 'application/json' },

    body: JSON.stringify(newUser),

  });

  if (!response.ok) {

    throw new Error('Failed to add user');

  }

  return response.json();

};


export function AddUser() {

  const [name, setName] = useState('');

  const mutation = useMutation(addUser, {

    onError: (error) => {

      alert(`Error: ${error.message}`);

    },

    onSuccess: () => {

      alert('User added successfully!');

    },

  });


  const handleSubmit = (e) => {

    e.preventDefault();

    mutation.mutate({ name });

    setName('');

  };


  return (

    <form onSubmit={handleSubmit}>

      <input

        type="text"

        value={name}

        onChange={(e) => setName(e.target.value)}

        placeholder="Enter user name"

        required

      />

      <button type="submit" disabled={mutation.isLoading}>

        {mutation.isLoading ? 'Adding...' : 'Add User'}

      </button>

    </form>

  );

}


Step 6: Providing User Feedback

Use error states and retry counts to display meaningful feedback to users.


Example: Display Retry Count

export function UsersList() {

  const { data, isLoading, isError, error, failureCount } = useQuery(['users'], fetchUsers, {

    retry: 3,

  });


  if (isLoading) return <p>Loading users...</p>;

  if (isError) {

    return (

      <div>

        <p>Error: {error.message}</p>

        <p>Retry attempts: {failureCount}</p>

      </div>

    );

  }


  return (

    <ul>

      {data.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}


Best Practices for Error Handling and Retries

Customize Retry Logic: Avoid retries for client errors (e.g., 400 status codes).

Use exponential backoff for server errors.

Graceful Error Handling:  Provide clear and actionable error messages for users.

Show retry counts to inform users about ongoing attempts.

Fallback UI: Render alternative content when errors persist.


In this tutorial, you learned:

  • How to handle errors in queries and mutations.
  • Configuring retry logic with custom behavior.
  • Providing meaningful user feedback for errors.

TanStack Query’s robust error handling and retry logic make it an excellent choice for creating resilient applications.  Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.

Comments

Popular posts from this blog

Integrating PHP with Message Queues RabbitMQ Kafka

FastAPI and UVLoop: The Perfect Pair for Asynchronous API Development

Konfigurasi dan Instalasi PostgreSQL Secara Lengkap di Windows Linux dan MacOS