This works with Meteor 2.x and renderToString from react-dom/server, with all initial data populated and rendered. Now that Meteor is moving away from Fiber, is there a way to use Meteor.callAsync inside a React Component and still achieve the same results with renderToString? At the moment, if I just use Meteor.callAsync(...).then(setData), renderToString returns with no initial data in the rendered HTML.
The current solution I have is to retrieve all the data in the onPageLoad callback. In this way, I need to check all the components and child components to see what data is being fetched, then retrieve them and pass them to the StaticRouter state. This will take a lot of effort so I am hoping there will be another solution.
That makes it very easy to deal with React components that also happen to be used in SSR. Also, no server code required to be exposed in the client which has its own issues related to security and client bundle size
You would need react server components for that, which is not supported by meteor.
Using Fiber’s in react server rendering was also not intended by react and lead to bugs in the past, altough it kindof worked.
If SSR is important, you may consider moving to a SSR framework like nextjs which supports server components. you can still keep meteor just for the api/backend
yes, but if you want to render components based on async data sources you either have to:
a) use server components
b) abstract your async call in a hook and continuously rerender the whole tree on the server until all your async calls are fullfilled. This is what apollo-client is doing when you want to server-render its data.
a) is not supported and b) needs a library that supports that pattern (or you write the nessecary abstraction yourself)
If I understood correctly, we can fetch this data in an SSR environment(with suspense). We have published the suspendable hooks.
I think using the useTracker with suspense in this scenario should work. Maybe @radekmie has some other ideas of how to make Meteor.callAsync work properly in an SSR environment.
SSR these days in react seems a little bit complicated
Unfortunately using Meteor.callAsync().then(setData) currently does not work for me (with Meteor version 2.14) as renderToString does not wait for any Promises to resolve. As for now, I am writing a custom hook for data retrieval on the server side and making a loop to keep rendering until all promises are fulfilled. This is the same as @macrozone mentioned above (solution b).
I’m not necessarily up-to-date with the latest React SSR state, but my take is that:
You don’t need React Server Components.
Using Suspense should work just fine, and there’s nothing Meteor-specific here.
Any Promise can suspend a component, but the management of throwing and caching them around is kind of tricky. We solved it for, e.g., useSubscribe, just as @grubba said, but Meteor.call would need a separate logic.
In the meantime, you could something like this package (I didn’t use it myself; just searched for “react suspense use promise”).
@grubba tried using Suspense in Meteor SSR and it worked as expected, i.e., sent a “shell” immediately and streamed data when it was ready.
I remember there was also an option to wait for everything before sending anything (i.e., no streaming).
This package @pbya suggested is kind of like Suspense, but globally, i.e., there’s no intermediate shell being sent to the client. (There’s also no error handling whatsoever.)
Suspense will send a ready HTML (i.e., with the data) if it’s loaded “fast enough” (I don’t think it’s not definied directly) or an empty shell (i.e., loading screens).
Here is the relevant function in React 18: renderToPipeableStream
There are two (2) important options:
Option 1:
optionalonShellReady: A callback that fires right after the initial shell has been rendered. You can set the status code and call pipe here to start streaming. React will stream the additional content after the shell along with the inline <script> tags that place that replace the HTML loading fallbacks with the content.
Option 2:
optionalonAllReady: A callback that fires when all rendering is complete, including both the shell and all additional content. You can use this instead of onShellReadyfor crawlers and static generation. If you start streaming here, you won’t get any progressive loading. The stream will contain the final HTML.
We still have not moved to React 18, with Meteor 3 being a blocker for us, so we have not tested these yet.
Looking at this, it looks like this requires support from webapp e.g. allowing pipe() function and response object to be accessible