Apollo-client: AC3: ApolloCache.write* no longer updates cache silently (breaking change)

Created on 10 May 2020  路  8Comments  路  Source: apollographql/apollo-client

Actual outcome:
Release AC3 v3.0.0-beta.40 and beyond:

  • ApolloCache.writeFragment triggers watched query broadcast: TRUE (breaking change)
  • ApolloClient.writeFragment triggers watched query broadcast: TRUE (as expected)

Release AC3 v3.0.0-beta.39 and before:

  • ApolloCache.writeFragment triggers watched query broadcast: FALSE (as expected)
  • ApolloClient.writeFragment triggers watched query broadcast: TRUE (as expected)

Intended outcome:

  • Known and consistent behavior of query broadcasts
  • A mechanism to update cache without triggering broadcasts (to improve performance in bulk update scenarios)
  • More generally - to understand the difference between calling ApolloClient.* and ApolloCache.* (and ideally have this in the documentation)

How to reproduce the issue:
The behavior change can be seen demonstrated in this react_apollo_error_template.

Repro steps:

  1. Click 'Update name via ApolloClient' button, and see that UI updates
  2. Click 'Update name via ApolloCache' button, and see that UI updates (breaking change)
  3. Update the @apollo/client dependency in package.json from 3.0.0-beta.46 to "3.0.0-beta.39"
  4. Click 'Update name via ApolloClient' button, and see that UI updates
  5. Click 'Update name via ApolloCache' button, and see that the UI does NOT update (as expected)

Related Questions
This issue, and review of the related source code, raised several questions I have been unable to find answers to in the documentation:

  1. Generally, what are the expected behavior differences between calling methods on the ApolloCache directly, and calling their counterparts via the ApolloClient (such as reads and writes), and what are the use cases for using one vs the other?

  2. The Cache has a (AFAIK undocumented) method: performTransaction. This seems useful for bulk-cache-update scenarios. Is this is intended for general use? And, why is it's counterpart not available on the ApolloClient?

  3. The ApolloClient and ApolloCache both seem to have their own independent queryWatch queues and broadcast mechanisms. This seems like it will have important (but hard to discern) behavior implications when using methods on ApolloCache directly vs via ApolloClient. Is there any documentation related to this, or can you describe the implications of this difference?

  4. (Bonus Question) When receiving the changed data via a watched query, the entire graph for the query is returned. Is there a mechanism to determine which pieces of the graph have changed? The ApolloClient seems to have some fairly fine-grained change-tracking internally, I just can't see an obvious way to access the details of what has changed.

Thanks

Versions
System:
OS: macOS 10.15.4
Binaries:
Node: 13.7.0 - /usr/local/bin/node
Yarn: 1.22.4 - ~/.yarn/bin/yarn
npm: 6.13.6 - /usr/local/bin/npm
Browsers:
Chrome: 81.0.4044.138
Safari: 13.1
npmPackages:
@apollo/client: 3.0.0-beta.46 => 3.0.0-beta.46

Most helpful comment

ApolloCache.writeFragment did _not_ trigger broadcasts to the client. However, it now does. Could you clarify if this is a bug, or as intended (and the way it will stay)?

You're right about the nature of the change (and thank you for making sure we knew about the difference), but this change was very much intentional, because it supports the general narrative that any updates to the cache should trigger corresponding updates in the UI.

We are considering adding a broadcast: boolean (default true) option to writeQuery and writeFragment to allow silencing the broadcast for individual writes. I think this option would serve the use cases that the client/cache distinction used to serve, without requiring any special understanding of the difference between cache.writeQuery and client.writeQuery. Thoughts?

All 8 comments

Yes, ApolloCache#performTransaction is the way to go here, if you need it. Wanting to delay/batch cache broadcasts is a relatively advanced use case, compared to the overwhelmingly common/default goal of wanting cache updates to be automatically broadcast to the client.

@benjamn - OK, thanks. Until AC3 v3.0.0-beta.39 ApolloCache.writeFragment did _not_ trigger broadcasts to the client. However, it now does. Could you clarify if this is a bug, or as intended (and the way it will stay)?

As a data-point, there is at least some expectation in the community that ApolloCache.writeFragment does not trigger broadcast, whereas ApolloClient.writeFragment does: https://spectrum.chat/apollo/apollo-client/apolloclient-vs-apolloclient-cache-methods~3ef19a32-73cd-4a1a-b944-50b1d0a41391

Personally, I think I prefer the consistency of having ApolloClient and ApolloCache related methods work the same way.

ApolloCache.writeFragment did _not_ trigger broadcasts to the client. However, it now does. Could you clarify if this is a bug, or as intended (and the way it will stay)?

You're right about the nature of the change (and thank you for making sure we knew about the difference), but this change was very much intentional, because it supports the general narrative that any updates to the cache should trigger corresponding updates in the UI.

We are considering adding a broadcast: boolean (default true) option to writeQuery and writeFragment to allow silencing the broadcast for individual writes. I think this option would serve the use cases that the client/cache distinction used to serve, without requiring any special understanding of the difference between cache.writeQuery and client.writeQuery. Thoughts?

Big +1 to a programmatic API to control broadcasts, as this is a needed feature for us as well.

I think it makes a lot of sense to not have an implicit difference between client.write* and cache.write* as it pertains to broadcasts, so the explicit ability to control that would be great.

Also, @benjamn you mention the cache.performTransaction API; how do you see the importance of / support for that API in a future where broadcast: false exists?

@benjamn - OK, thanks for the clarification.

We are considering adding a broadcast: boolean (default true) option to writeQuery and writeFragment to allow silencing the broadcast for individual writes. I think this option would serve the use cases that the client/cache distinction used to serve, without requiring any special understanding of the difference between cache.writeQuery and client.writeQuery. Thoughts?

Yes, would welcome this addition!

My primary question has been answered, and it looks like the feature to add a broadcast boolean to cache.write* and client.write* has been added to the roadmap (+1). Closing ticket.

Opened PR #6288 to implement the options.broadcast idea for cache.writeQuery, cache.writeFragment, and cache.evict.

Generally, what are the expected behavior differences between calling methods on the ApolloCache directly, and calling their counterparts via the ApolloClient (such as reads and writes), and what are the use cases for using one vs the other?

It looks like Apollo is moving towards a standardization of the client/cache behaviour (+1 for this), but that makes the previous point even more compelling: what are the use cases for using client vs cache?

Was this page helpful?
0 / 5 - 0 ratings