react-query and New Suspense SSR Architecture in React 18 #2942
Replies: 4 comments 10 replies
-
hydration per query looks like a cool thing for the new React 18 Suspense SSR approach. Do you have a proposal on how to integrate this directly with react-query ? One question: Is it "safe" (= allowed by the rules of react with regards to concurrent mode) to call |
Beta Was this translation helpful? Give feedback.
-
I don't have time to dive into the details right now, but I think the basic approach you are describing is the correct one when it comes to supporting Suspense+Streaming SSR. There's some notes about this in the pretty new Library Upgrade Guide: <script> (e.g. SSR frameworks) First:
I think this is what we should aim for when it comes to initial React 18 support. I think this is a matter of capturing an immutable snapshot of the initial React Query cache on the client and passing that as the Note that this would not support the current RQ Suspense-API with streaming and progressive hydration. The Suspense API with streaming but without progressive hydration should work though, since the entire cache would then be available before React hydration starts on the client. Later:
This is probably worth experimenting with when step 1 is in place, knowing that experimentation might be superseeded by an official solution later. |
Beta Was this translation helpful? Give feedback.
-
@TkDodo hi) As I can see from https://github.com/tannerlinsley/react-query/blob/master/src/core/hydration.ts there is a dehydration process for mutations. Do you know any cases, where mutations intended dehydration? |
Beta Was this translation helpful? Give feedback.
-
This works well for me: |
Beta Was this translation helpful? Give feedback.
-
First of, I want to recap, how it works right now, with React 17 and older. I mean React + react-query + SSR. The main idea is described here, in the docs — Prefetch the query on the server, dehydrate the cache and rehydrate it on the client.
This approach works well with sync version of React render. But, new suspense SSR architecture is quite different. The base idea is the same — prefetch, dehydrate and rehydrate. The main question is about timings. So, I think, that I have a solution, that I'd like to discuss.
There is a link to a repository and a link to this project in action.
If you will open the last link, you will see, that where is a loader on a place, where newsList component has to be. After several seconds, the component source code and a data for it will be loaded. The whole document will be ready. React-query is used here as a data-fetcher and store, React.lazy for code-splitting. The main guy is React.Suspense.
As you can see from that demo, there is no useless requests from the client side. Dehydrate and rehydrate works perfectly, even renderToPipeableStream is used. So, how it's made?
renderToPipeableStream returns just two methods right now: pipe and abort. Abort is used to abort current request (obviously). Pipe is the most interesting guy here. We can pass any writeable stream into that pipe. It can be res writeable from ExpressJS in my implementation. In that case, there will be a useless request for the data for newsList, cause there is no any dehydrated data in a response. So, how to fix it? How to add a dehydrated data to the stream?
I've started with that discussion — Library Upgrade Guide: <style> (most CSS-in-JS libs). Yeap, it is about CSS) But, there is quite interesting idea. We can wrap a response writeable with our own wrapper, where we can add any data to that stream. So, I've created my own wrapper for CSS in JS. And I added some logic to append all data from react-query cache into the stream. You can check out it here.
Every time, when React tries to write some HTML into the stream, we will try to dehydrate query into the stream, if there is anything in the react-query cache. The data will be wrapped with a script, which will be executed as soon, as it will be on a page. React won't render anything at that moment. So, there won't be any errors about server and client HTML mismatch. There won't be a general dehydratedState, but each query will have its own object inside the HTML.
Now, let's talk about client side. Right now, with React 17, we must use component with dehydratedState to start the client side version of the app. But we can not use it with after renderToPipeableStream, cause there is no any general dehydratedState. There is one simple approach, how to fix it.
I've created a simple wrapper around react-query — useCommonAppQuery. It has several purposes. The main is to read a dehydratedState for the current query. There is a function for it — getDehydratedQueryStateFromDom. We will just read the data from window and hydrate the query by calling
hydrate
method from react-query for the current query. And voila, everything works like a charm!So, what do you think about that approach? Do you have any other ideas, how to hydrate/dehydrate with new Suspense SSR Architecture in React 18?
Beta Was this translation helpful? Give feedback.
All reactions