Search code examples
typescriptfetch-apireact-query

Under which conditions is the AbortSignal in the TanStack useQuery function undefined?


The documentation of TanStack React Query (https://tanstack.com/query/v4/docs/react/guides/query-cancellation) explains how the queryFn property can receive an object with a signal property, which can be used to cancel a query, like in this example.

const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const todosResponse = await fetch('/todos', {
      signal,
    })
    const todos = await todosResponse.json()
    const todoDetails = todos.map(async ({ details }) => {
      const response = await fetch(details, {
        signal,
      })
      return response.json()
    })

    return Promise.all(todoDetails)
  },
})

The problem is that, with TypeScript, this signal property is type as AbortSignal | undefined. With the API that I am using, having to conditionally check if the signal is set adds a lot of complexity to the code so it would be very helpful to know under which conditions this signal object is not defined. Any ideas would be much appreciated.


Solution

  • react-query v4.29.11

    The signal will exist if the AbortController class exists. Let's take a look at the source code:

    The signal property is added to the query function context by addSignalProperty() function, see below:

        const addSignalProperty = (object: unknown) => {
          Object.defineProperty(object, 'signal', {
            enumerable: true,
            get: () => {
              if (abortController) {
                this.abortSignalConsumed = true
                return abortController.signal
              }
              return undefined
            },
          })
        }
    
        addSignalProperty(queryFnContext)
    

    The abortController is got by getAbortController() function

    export function getAbortController(): AbortController | undefined {
      if (typeof AbortController === 'function') {
        return new AbortController()
      }
      return
    }
    

    That's why the TS type of signal property is: AbortSignal | undefined.

    The official documentation also mentions it.

    The AbortController API is available in most runtime environments, but if the runtime environment does not support it then the query function will receive undefined in its place. You may choose to polyfill the AbortController API if you wish, there are several available.