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
717 stars 22 forks source link

Add ability to load on mount only if no cache #46

Closed Grsmto closed 3 years ago

Grsmto commented 4 years ago

Hi Jayden, First, thanks for the great work on the lib!

I was wondering if there is a way to cache a query, even when after unmounting: So for example I have a query being triggered when my page mounts, if I navigate out and just hit browser back button, the query is triggered again. In that case I would like the cache to take over instead of refetching the content.

Is that something that could be easily implemented?

Thanks!

Edit: related to this but not entirely, I think it would be great to be able to customize firstRenderDate timeout, right now it's hardcoded do 1000ms but I had a case where I had a rerender happening after 1s and it was triggering an unecessary refetch. Being able to force the cache to be used per hook would solve this issue as well!

jaydenseric commented 4 years ago

I was wondering if there is a way to cache a query, even when after unmounting

Queries are always cached. I think what you are wondering, is if there is a way to only fetch a query the first time, and from then on, just render the cache and don't refetch. This is the way Apollo works out of the box, and it's what people coming from Apollo are familiar with.

In short, no. After a lot of experience with the cache based approach in Apollo, I came to see it as not a good idea. I have given 45 minute talks on why it's not a good idea, and why graphql-react has the API design that it does - I don't really have the time to explain it in depth here now, sorry.

The graphql-react API makes it easy to only query data you need to render a given UI (e.g. no need to query id fields for caching, or have big mutation payloads to attempt to guess and update the cache used in other screens) and render that cache optimistically while it's refreshing. If a mutation happens that you know causes cache to become stale, the best approach is to trigger the mounted components to all refresh, and optionally wipe the cache for queries that are not currently mounted too. The Apollo approach of attempting to manually update the global cache after mutations is totally impractical, and in many situations, impossible. It has led to spaghetti code (no separation of concerns between components), overfetching, and cache staleness bugs every in every single Apollo app I have ever worked on.

I have discussed with friends about updating the graphql-react API to have a fetch-if-no-cache type option, but I honestly don't want to encourage the pattern.

In the old school server side rendered web, users expected that when they navigate to a page, they are loading a fresh version of that page. Too many apps today allow you to navigate around without actually refreshing anything and users notice the staleness. E.g. they create a thing, navigate back to a dashboard, and notice their activity statistics have not updated — so they manually refresh the page. These sorts of problems.

jaydenseric commented 3 years ago

Thanks for the original suggestion, but closing because this isn’t a pattern I’m keen to support out of the box.

The new graphql-react v13 API has separate React hooks for the different concerns of reading from (i.e. useCacheEntry) vs loading cache entries. A custom React hook could be created as an alternative to useAutoLoad, which internally uses the useLoadOnMount hook:

https://github.com/jaydenseric/graphql-react/blob/4ea092280531b4ef62adb02d365e4fbb23e418d8/public/useAutoLoad.js#L62

Instead of always loading on mount, a custom useLoadOnMountIfNoCacheEntry hook could be used to check first if there is a cache entry for the given cache key before loading.