SalesforceCommerceCloud / pwa-kit

React-based JavaScript frontend framework to create a progressive web app (PWA) storefront for Salesforce B2C Commerce.
https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pwa-kit-overview.html
BSD 3-Clause "New" or "Revised" License
283 stars 130 forks source link

[FEATURE] withReactQuery - add an option to seed the queryClient or to prefetch query #1806

Closed sorioinc closed 1 week ago

sorioinc commented 4 months ago

In SSR, to limit the amount of calls from different use queries; we have an init useQuery that retrieves all the initial data. Subsequent queries will attempt to retrieve the data from the cache, but if it's not available, they will make an api call.

Since withReactQuery will populate the queryCache in the prepass process, but not execute the fetch immediately, the dependent queries won't find anything in cache and hence decide to make an api call instead. There's no option to re-render and make the dependent queries to reevaluate themselves.

Can there be a way to hint the withReactQuery hoc to execute queries in priority or an option to prefetch certain queries before the prepass (so the cache is already there)?

Maybe provide another param that's an async delegate which receives the queryClient then we can call a prefetch inside. I also tried setting the __queryClient in locals in the AppConfig, but it didn't quite work.

withReactQuery(AppConfig, options, async () => {
    await queryClient.prefetchQuery({
        queryKey: ['todos'],
        queryFn: fetchTodos,
    })
})
bendvc commented 4 months ago

Hi @sorioinc

The way react query works within the PWA-Kit is intentional, and the reason we don't have serialization of api calls via the react query is that doing so would be detrimental to rendering performance as you'd be introducing a single link in the render pipeline that is blocking all other requests from being made.

May I ask what the exact use case you are hoping to solve is? There might be other things that you can do to overcome the limitations that you ran into. For example, have you thought about using SCAPI hooks to enhance the data that is being returned via the API, in that way you can serialize things on the server.

Another option is to use the useCommerceApi hook to get direct access to the api client, once you have access to the client you can build your own query using useQuery directly. This would also allow you to serialize calls inside the fetch function. There would be some limitations as to where the data ends up being cached, but nothing you couldn't work around I don't think.

Ben

sorioinc commented 4 months ago

Thanks for your reply @bendvc,

Our use case is similar to the following, since we are using react-query for our own bff endpoints:

Imagine endpoints on our custom BFF:

React Queries related in this order:

In CSR, it works because the useQuerySSRInitialVals is called higher in the component tree; when the user journey eventually takes them to banners or promotions, the useQuery will check useQuerySSRInitialVals for the cached value and decide to do network request from the browser.

In SSR, on each document request; react-ssr-prepass, will discover the useQueries (including useQuerySSRInitialVals) and later resolve them concurrently. This means we get five network requests from the server every single time. In the server I'm looking to avoid network requests for promotions, banners, and preferences, because those values can be gathered from the initial_values endpoint already, so if useQuerySSRInitialVals can be executed before the prepass or a way to seed the cache then I would be able to achieve that.

Right now I made a little hack, by locating useQuerySSRCache as higher in the component tree execution as possible, then setting initialData with data that I store in a singleton with initial_values endpoint result from previous document requests. This ends up making only on one network request afterwards

After the server boots up, first document request from user browser will make five network requests from the server to external resources:

Second request from user browser, will produce network calls for:

bendvc commented 3 months ago

In SSR, on each document request; react-ssr-prepass, will discover the useQueries (including useQuerySSRInitialVals) and later resolve them concurrently. This means we get five network requests from the server every single time. In the server I'm looking to avoid network requests for promotions, banners, and preferences, because those values can be gathered from the initial_values endpoint already, so if useQuerySSRInitialVals can be executed before the prepass or a way to seed the cache then I would be able to achieve that.

Have you considered having the hooks you don't want to run on the server be disabled? Specifically for the data that you don't want, promotions, banners, and preferences.