How to stop Redux RTK query from retrying on error
Asked Answered
S

6

6

I have some requests which may return 404s. When they do, RTK query will send retries, resulting in hundreds of failed requests. Why is it trying to refetch on error and what can I do?

Subdue answered 3/2, 2022 at 16:58 Comment(0)
H
5

If your endpoint is in error, RTK Query's useQuery will send a request in two situations:

  • you change the argument (that would always result in a new request)
  • you mount a component using this useQuery.

So without seeing your code, I would assume that your component re-mounts somehow and thus leads to another request after mounting.

Hideandseek answered 3/2, 2022 at 21:57 Comment(1)
in my case it was maxRetries:0 not working as expected, which is solved in v1.9.1Pretorius
P
4

you can limit the number of retries that rtk automatically does by using the property maxRetries inside your end point.

See the retry documentation here

import { createApi, fetchBaseQuery, retry } from 
'@reduxjs/toolkit/query/react'

// maxRetries: 5 is the default, and can be omitted. Shown for 
documentation purposes.
const staggeredBaseQuery = retry(fetchBaseQuery({ baseUrl: '/' }), {
  maxRetries: 5,
})
export const api = createApi({
  baseQuery: staggeredBaseQuery,
  endpoints: (build) => ({
    getPosts: build.query({
      query: () => ({ url: 'posts' }),
    }),
    getPost: build.query({
      query: (id) => ({ url: `post/${id}` }),
     extraOptions: { maxRetries: 5 }, // You can override the retry behavior on each endpoint
    }),
  }),
})

export const { useGetPostsQuery, useGetPostQuery } = api
Pandect answered 5/9, 2022 at 13:58 Comment(0)
C
0

As docs say, for custom error handling we can use queryFn:

One-off queries that use different error handling behaviour

So if, for any reason, you want to cache request on error, you can do:

getPokemon: build.query<Pokemon, string>({
  async queryFn(name, api, extraOptions, baseQuery) {
    const result = await baseQuery({
      url: `https://pokeapi.co/api/v2/pokemon/${name}`,
      method: 'GET'
    });

    if (result.error?.status === 404) {
      // don't refetch on 404
      return { data: result.data as Pokemon };
    }

    if (result.error) {
      // but refetch on another error
      return { error: result.error };
    }

    return { data: result.data as Pokemon };
  }
}),
Cristobal answered 22/6, 2022 at 12:23 Comment(0)
P
0

You need to customize your createApi function. you can stop permanently retries with setting unstable__sideEffectsInRender property to false

import {
  buildCreateApi,
  coreModule,
  reactHooksModule,
} from '@reduxjs/toolkit/dist/query/react';

const createApi = buildCreateApi(
  coreModule(),
  reactHooksModule({ unstable__sideEffectsInRender: false })
);

export default createApi;
Physicality answered 24/1, 2023 at 23:40 Comment(0)
P
0

const {isError} = useQuery();

if (isError) show error message else render the components.

check the isError property return by useQuery before the component renders. If API failed then 'isError' becomes true then renders the components, which will prevent multiple API requests for the failed scenario.

Purgative answered 27/7, 2023 at 6:29 Comment(0)
W
0

Apr 2024 answer: You can customize the retryCondition:

import { Mutex } from 'async-mutex'
import {
  BaseQueryApi,
  BaseQueryFn,
  FetchArgs,
  fetchBaseQuery,
  FetchBaseQueryError,
  retry,
} from '@reduxjs/toolkit/query'
import { RootState } from '@common/store'
import {
  BaseQueryArg,
  BaseQueryExtraOptions,
} from '@reduxjs/toolkit/dist/query/baseQueryTypes'
import { RetryOptions } from '@reduxjs/toolkit/dist/query/retry'

// HERE
const customRetryCondition = (
  error: FetchBaseQueryError,
  args: BaseQueryArg<BaseQueryFn>,
  {
    attempt,
    extraOptions: { maxRetries } = {},
  }: {
    attempt: number
    baseQueryApi: BaseQueryApi
    extraOptions: BaseQueryExtraOptions<BaseQueryFn> & RetryOptions
  },
) => {
  // retry on default condition
  const defaultCondition = attempt <= (maxRetries || 0)
  if (defaultCondition) {
    return true
  }

  // retry on network's errors
  if (error.status === 'FETCH_ERROR' || error.status === 'TIMEOUT_ERROR') {
    return true
  }

  // other case (application logic error), don't retry
  return false
}

const baseQuery = retry(
  fetchBaseQuery({
    prepareHeaders: (headers, { getState }) => {
      const {
        userAuth: {
          token: { accessToken },
        },
      } = getState() as RootState
      const token = accessToken
      if (token) {
        headers.set('authorization', `Bearer ${token}`)
      }

      return headers
    },
    baseUrl,
  }),
  {
    maxRetries: 5,
    // HERE
    retryCondition: customRetryCondition as unknown as undefined, // avoiding typescript error
  },
)
Wilda answered 28/4 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.