Relay: [modern] Proposal: support of forceFetch on QueryRenderer

Created on 23 Apr 2017  路  9Comments  路  Source: facebook/relay

In classic Relay, it was possible to define forceFetch on RootContainer. Now, when RootContainer is no longer available, it would be great if we can define it on QueryRenderer. This would allow us to split parts of app, which must always fetch data from part, where data can be cached.

Most helpful comment

@josephsavona The main issue with request based caching is that it doesn't work well with mutations. Not sure if there is something I didn't think of but right now I'm invalidating the whole cache for any mutation to avoid stale data.

It would be nice to have some API on QueryRenderer to check the normalized cache and load from that if the data is available as well as some control of how caching is handled like the QueryResponseCache (ttl, size).

I think to have a complete API there are 3 modes that are useful:

  • No cache / network layer cache (current behavior)
  • Use cache + refetch (show the cached data if available but still send the query to the network layer)
  • Use cache (no query to network layer if all data is available from cache)

I think everything exists right now to be able to do that it is just not exposed on QueryRenderer which is an issue since to implement this right now we have to vendor a modified copy of QueryRenderer.

All 9 comments

Relay Modern does not diff queries against the store, and by default does not render from cached data. In other words, QueryRenderer does the equivalent of force-fetching by default. It also accepts a cacheConfig option (shape is {force: boolean}) that can be interpreted by your network layer. Again, by default most network layers will ignore this and always force fetch. But a network layer is free to implement request/response caching and enable it so long as cacheConfig.force is false.

Hm, interesting. Thanks @josephsavona, going to play more. 馃憤

@svrcekmichal note that if you are using QueryRenderer in compat mode - with a classic store instance - that it will do diffing by default, since that's the default behavior in Relay classic. You can use cacheConfig={force: true} to bypass that.

@josephsavona Thanks, I'm not. I'm only playing with new modern version on my little project. Btw. what are the reasons behind this change? In classic relay, I've been having whole app with forceFetch because of no subscribes. Even when I played with subscribes, when I fetched same data and subscribed to their changes, after navigation to another page and unsubscribe, when I get back to previous view data was wrong ( which was of course fine because no subscription was kept there, but bad because of invalid data).Are the reasons similar? Btw. have you got some statistic how much more requests are you doing to server after this change?

The main reason is that Relay Modern uses static queries for performance. GraphQL text is parsed at build time into artifacts that represent queries, fragments, etc. This moves the work of constructing queries and printing them to strings from runtime to build time. Relay classic's diffing relied on runtime modification of queries, so it didn't fit well in this model.

A secondary benefit of removing diffing is predictability: Relay Modern will either fetch the query basically as you wrote it (possibly with some optimizations that don't affect the shape of the result) or not fetch it at all (if you do caching in the network layer), which reduces the unpredictability of diffing removing different fields based on what you happened to have fetched when a query is executed.

I'm confused about this - does that mean there is also no way to read the entire query from the cache either? I agree modifying queries doesn't make sense, but that doesn't mean you can't read the data from the cache with the whole query right?

@stubailo Yup, it's possible to read the entire query from the cache in full (let operation = getOperationSelector(query); let result = lookup(operation.root);). There's also a function to traverse the query against the cache to determine whether the query can be fulfilled locally - this isn't exported yet but probably should be (specifically, we should add a wrapper method for RelayAsyncLoader.check() to the Environment API)

What I was referring to is that the public APIs for fetching queries (QueryRenderer, refetch container's refetch(), pagination container's loadMore()) don't currently check the query against the cache, they just fetch it in full. But the primitives are there to support other patterns.

@josephsavona The main issue with request based caching is that it doesn't work well with mutations. Not sure if there is something I didn't think of but right now I'm invalidating the whole cache for any mutation to avoid stale data.

It would be nice to have some API on QueryRenderer to check the normalized cache and load from that if the data is available as well as some control of how caching is handled like the QueryResponseCache (ttl, size).

I think to have a complete API there are 3 modes that are useful:

  • No cache / network layer cache (current behavior)
  • Use cache + refetch (show the cached data if available but still send the query to the network layer)
  • Use cache (no query to network layer if all data is available from cache)

I think everything exists right now to be able to do that it is just not exposed on QueryRenderer which is an issue since to implement this right now we have to vendor a modified copy of QueryRenderer.

@janicduplessis can you explain how you invalidated the whole cache, I'm hitting that exact scenario where a mutation makes the cache invalid. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

HsuTing picture HsuTing  路  3Comments

piotrblasiak picture piotrblasiak  路  3Comments

jstejada picture jstejada  路  3Comments

scotmatson picture scotmatson  路  3Comments

MartinDawson picture MartinDawson  路  3Comments