Open sazzer opened 2 years ago
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.
Awesome :)
Is there anything that documents upcoming changes? I did a search for "Suspense" in the issues but nothing that looked relevant came up.
Not really, but you will need the new api.getRunningQueryPromise
function as a base foundation.
Looks like 1.7 released last month, and the suspense is killing me ;)
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.
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 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".
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
}
}
}
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)
Relevant thoughts over in the React Query repo:
https://github.com/TanStack/query/discussions/4623
Also probably outdated discussion on Twitter:
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