jaydenseric / graphql-react

A GraphQL client for React using modern context and hooks APIs that is lightweight (< 4 kB) but powerful; the first Relay and Apollo alternative with server side rendering.
https://npm.im/graphql-react
MIT License
718 stars 22 forks source link

Examples of SSR cache hydration #25

Closed chrischen closed 5 years ago

chrischen commented 5 years ago

I'm hydrating the cache in the client browser as follows:

const graphqlClient = new GraphQL({ cache:  window.__GRAPHQL_STATE__ })

window.__GRAPHQL_STATE__ is an object like this:

{
  '18s6t0k': {
    'data': ...
  }
}

However I'm still seeing the graphql requests hitting the graphql server two times, once on server load and once on client. I've tried disabling the call on client/server code each independently and can confirm it's not simply just running two times.

jaydenseric commented 5 years ago

Your hydration is probably working, it's just that the client fetches fresh cache when mounting, see https://github.com/jaydenseric/graphql-react/issues/4 (although the Query component no longer exists, useGraphQL has a similar behavior).

Hydrating the data from the server is still important so that the SSR content is not replaced with only loading state when React mounts the components.

jaydenseric commented 5 years ago

As a potential enhancement, cache values could have a new dateFetched property with a Date instance denoting when the cache was fetched. That way useGraphQL could have a new option, cacheExpiresMs (perhaps a better name) that would be how many milliseconds old cache can be to be reused, otherwise a load() will fetch fresh cache.

That way if the page load is quicker that the cacheExpiresMs, cache is reused on the client without a fetch.

chrischen commented 5 years ago

ApolloClient seems to have an ssrMode flag that prevents fetching again on initial load.

Cache expiry check Pros:

Cache expiry check cons:

jaydenseric commented 5 years ago

Apollo doesn't fetch when the app mounts in the browser because it doesn't fetch queries when components mount like this library does. Apollo has a lot of issues with stale data as you navigate around an app that this library doesn't.

In the past, I had decided that the client updating it's cache on first mount is a worthwhile tradeoff.

jaydenseric commented 5 years ago

It would be great if there was a way React components could tell that that they are mounting in the browser for the first time after SSR, but I can't think of a way.

chrischen commented 5 years ago

What does the current cache do if it always reloads from network?

jaydenseric commented 5 years ago

It doesn't always load from the network; if the useGraphQL options loadOnMount is false it won't.

A central cache is important for lots of reasons.

I already explained that hydrating cache from SSR is important so that when React mounts on the client, the data can be used to cause an identical initial render. Just disable client hydration an you will see the difference: The client will wipe away most of the HTML the server rendered and display only loading states everywhere.

While navigating around your app, cache from the last time the component loaded can be rendered optimistically so the user doesn't always see loading spinners. If the cache that loads is different the component re-renders with the fresh cache.

If you have multiple components with the same query that loads on mount, they will all share one fetch request instead of a request each.

If you have multiple components with the same query that load on demand, loading one will cause cache to render in all of them.