Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Retries React Query

April 14, 2021
reacttypescript

This is another post in a series of posts using React Query with TypeScript.

Previous posts:

In this post, we will explore the retry feature in React Query which is useful when fetching data over flakey connections. We start by exploring the default retry behaviour before moving on to implementing our own custom retry logic.

App state

Our start point

We will start from the code we finished in the Getting started post.

This is what the fetching function looks like:

type Character = {
  name: string;
};
type Params = {
  queryKey: [string, { id: number }];
};
async function getCharacter(params: Params) {
  const [, { id }] = params.queryKey;
  const response = await fetch(`https://swapi.dev/api/people/${id}/`);
  if (!response.ok) {
    throw new Error("Problem fetching data");
  }
  const character = await response.json();
  assertIsCharacter(character);

  return character;
}
function assertIsCharacter(character: any): asserts character is Character {
  if (!("name" in character)) {
    throw new Error("Not character");
  }
}

This is the component containing the query:

export function App() {
  const { status, error, data } = useQuery<Character, Error>(
    ["character", { id: 1 }],
    getCharacter
  );

  if (status === "loading") {
    return <div>...</div>;
  }
  if (status === "error") {
    return <div>{error!.message}</div>;
  }
  return data ? <h3>{data.name}</h3> : null;
}

Default retry behavior

Let’s change the fetch call so that the resource isn’t found:

const response = await fetch(`https://swapi.dev/api/unknown/${id}/`);

The fetching function will now raise an error. Let’s see what happens:

Default try behavior

The fetching function is retried up to three times before it gives up. The retry delay gets longer for each subsequent retry - 1 sec, 2 secs, and 4 secs.

Preventing retries

The retry behaviour can be overriden with the retry option. To turn retries completely off we set retry to false:

const { status, error, data } = useQuery<Character, Error>(
  ["character", { id: 1 }],
  getCharacter,
  {    retry: false  });

Changing the number of retries

To change the number of retries we can set retry to the number we require:

const { status, error, data } = useQuery<Character, Error>(
  ["character", { id: 1 }],
  getCharacter,
  {
    retry: 5  }
);

The above query is retried up to 5 times.

Retrying based on the fetching error

retry can also be set to a function. This is useful when the retry logic is dependent on fetching error.

Before we try this out, let’s create a class that extends the standard Error class to store the HTTP status code in the error:

class FetchError extends Error {
  constructor(public message: string, public statusCode: number) {
    super(message);
  }
}

We can now change the fetching function to raise this type of error:

async function getCharacter({
  queryKey,
}: {
  queryKey: [string, { id: number }];
}) {
  ...
  if (!response.ok) {
    throw new FetchError("Problem fetching character", response.status);  }
  ...
}

We can now implement a retry function as follows:

const { status, error, data } = useQuery<Character, FetchError>(
  ["character", { id: 1 }],
  getCharacter,
  {
    retry: (failureCount, error) =>        error.statusCode === 404 && failureCount <= 3 ? true : false,  }
);

The retry function takes in the number of failures and the error object thrown from the fetching function.

The retry function is expected to return a boolean indicating whether a retry should occur.

In this example, the query is retried up to 3 times if the resource isn’t found.

Changing the retry delay

The retry delay can be changed with the retryDelay option.

const { status, error, data } = useQuery<Character, FetchError>(
  ["character", { id: 1 }],
  getCharacter,
  {
    retry: (failureCount, error) =>
        error.statusCode === 404 && failureCount <= 3 ? true : false,
    retryDelay: retryCount => retryCount === 0 ? 1000 : 5000  }
);

retryDelay is set to a function that takes in the retry count and returns the number of milliseconds to wait until the next retry.

The first retry delay is 1 second and then 5 seconds for subsequent retries in the above example.

Nice. 😊

The code in this post is available in CodeSandbox at https://codesandbox.io/s/react-query-retry-3sy9u?file=/src/App.tsx

Did you find this post useful?

Let me know by sharing it on Twitter.
Click here to share this post on Twitter

If you to learn more about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Find out more