redux-toolkit icon indicating copy to clipboard operation
redux-toolkit copied to clipboard

Support for Suspense in RTK Query

Open sazzer opened this issue 4 years ago • 11 comments
trafficstars

Redux Toolkit and RTK Query are awesome, and have really made it easier to get things working quickly and well.

What would be really good though is to have support for the React Suspense API, so that we can render a fallback component whilst data is loading without needing to handle it in-component with the isLoading flag.

It's not a huge deal, but it just makes things that little bit neater to have a wrapper around the component that handles this instead.

Cheers

sazzer avatar Oct 05 '21 10:10 sazzer

RTK 1.7 will have the necessary internal changes for that. (see #1277) After that we'll probably release an external package marked as "unstable" with a module for suspense hooks.

phryneas avatar Oct 05 '21 11:10 phryneas

Awesome :)

Is there anything that documents upcoming changes? I did a search for "Suspense" in the issues but nothing that looked relevant came up.

sazzer avatar Oct 05 '21 11:10 sazzer

Not really, but you will need the new api.getRunningQueryPromise function as a base foundation.

phryneas avatar Oct 05 '21 11:10 phryneas

Looks like 1.7 released last month, and the suspense is killing me ;)

AndrewCraswell avatar Jan 09 '22 12:01 AndrewCraswell

All the building blocks for it are there now, so you could build your own hooks or RTK Query plugin for it at this time.

But I don't have the capacity to build anything at the moment. If you want this functionality in the near future, it probably has to come from the community.

phryneas avatar Jan 09 '22 12:01 phryneas

Moving loading states out of components into Suspense looks like the way forward and there seems to be a lot of work going in other frameworks to accomodate. Will there be any first-class Suspense support in RTK Query in the future?

laurencefass avatar Jun 15 '22 09:06 laurencefass

@laurencefass yes, for example in the PR #2245 directly above your answer. The point is though, that Suspense has not officially been declared as "ready for use with client-side libraries" by the React team yet, but only as "ready for use if you use an opinionated framework like next if that supports it".

phryneas avatar Jun 15 '22 09:06 phryneas

I ended up writing my own quick-and-dirty hook using api.getRunningQueryPromise as @phryneas mentioned in previous comments:

// Simplified version of QueryActionCreatorResult
interface WrappedData<T extends unknown> {
  data: T
}

export const useRtkQueryResource = <T extends unknown>(api: unknown, endpointName: string): never | (() => T) => {
  // @ts-ignore
  const apiEndpointQuery = api.endpoints[endpointName]
  // @ts-ignore
  const useEndpointQuery = apiEndpointQuery?.useQuery
  
  const { data } = useEndpointQuery(null) as WrappedData<T>

  // @ts-ignore
  let promise = api
    // @ts-ignore
    .util
    .getRunningOperationPromise(endpointName, null) as PromiseLike<WrappedData<T>>|undefined

  // Promise is undefined when data is there cached locally already,
  // in this case let's have a promise resolved with the locally cached data
  if (promise === undefined) {
    promise = new Promise(
      (resolve, reject)=> {
        if (data !== undefined) {
          resolve({ data })
        } else {
          reject("Cannot get RTK Query promise and there is no data loaded yet")
        }
      }
    )
  }
  
  let status = 'pending'
  let response: T

  promise.then(
    (res: WrappedData<T>) => {
      status = 'success'
      response = res.data
    },
    (err) => {
      status = 'error'
      response = err
    },
  )

  return () => {
    switch (status) {
      case 'pending':
        throw promise
      case 'error':
        throw response
      default:
        return response
    }
  }
}

luke10x avatar Jun 16 '22 18:06 luke10x

For the record, I just built a proof of concept "Suspense Cache" using RTK Query. Not final yet, but you can see the PR here:

https://github.com/replayio/devtools/pull/7205

This lets you do:

function MyComponent() {
  const data = getDataFromSuspenseCache()
  
  // render here
}

while encapsulating the whole "throw a promise or return the data" thing.

(fixed the wrong PR link)

markerikson avatar Jun 22 '22 20:06 markerikson

Relevant thoughts over in the React Query repo:

https://github.com/TanStack/query/discussions/4623

Also probably outdated discussion on Twitter:

https://twitter.com/arnbzn/status/1435529763893006336

markerikson avatar Dec 12 '22 15:12 markerikson