question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Writing directly to cache does not synchronously update `useQuery` return values

See original GitHub issue

Intended outcome: When I write directly to my InMemoryCache, I want the updated data to be used the next time my React component renders.

Actual outcome: When I write directly to my InMemoryCache, there is a short time period in which my React component uses old data.

This can cause bugs if you have code that assumes that writing to the cache immediately updates the relevant useQuery instances. Here’s a real world example: imagine the user has just joined an organization in a multi-tenant application. The code might do the following:

  1. Write to the cache to add a new organization to currentUser.organizations.
  2. Use React Router’s navigate function to go to the new organization’s URL.
  3. (Now the code explodes because the URL specifies an organization which is not contained in currentUser.organizations.)

How to reproduce the issue:

  1. View my CodeSandbox.
  2. Open the console in the right pane.
  3. Click “Add 1 to rates”.

You should get output similar to the following. Notice how “Direct read” and “useQuery” are out of sync in the first rerender.

Direct read: 4.673000143247005
useQuery: 3.6730001432470056 
------------------- 
Direct read: 4.673000143247005
useQuery: 4.673000143247005
------------------- 

Versions image

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
srmaguracommented, May 25, 2022

Are you just looking for a quick way to restore previous behavior, or do you foresee wanting cache updates to be applied synchronously in the longer term?

I can foresee wanting cache updates to be applied synchronously in the longer term. Synchronous updates are useful for situations like the one I described in the original post. There is also just less potential for bugs when things are synchronous. Though, I understand that batching updates can be more efficient.

I think the bigger issue is that a method like cache.modify appears to be synchronous since it does not return a Promise, but its actual behavior is asynchronous. modify updates the cache immediately, but the method is still asynchronous when viewed in the larger context of a React application.

This discrepancy between the method’s expected and actual behavior could be remedied in two ways:

A) Make cache.modify update all observers synchronously, OR B) Make cache.modify return a Promise that resolves once all observers have been updated.

Since Apollo Client is (more or less) a global state manager, it could be useful to look at what other global state management libraries do. For example, I am pretty sure that Redux + react-redux does not do any batching unless you explicitly use the batch function that they export.

1reaction
srmaguracommented, May 25, 2022

Our real code is using cache.modify, and there does not seem to be an equivalent method on client. Here’s a workaround:

client.cache.modify(...);

(client as any).queryManager.broadcastQueries();

The as any is necessary because ApolloClient.queryManager is a private property. But it’s only private from TypeScript’s point of view — the property is still accessible at runtime.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Advanced topics on caching in Apollo Client
By default, the contents of your cache are immediately restored asynchronously, and they're persisted on every write to the cache with a short...
Read more >
Fetch, Cache, and Update Data Effortlessly with React Query
React Query caches data based on the query key so choosing a proper key for your query is very important. You need to...
Read more >
react-query getting old data - Stack Overflow
My updateItem API function returns the single updated item from the server. I used setQueryData to solve this.
Read more >
QueryClient | TanStack Query Docs
setQueryData is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it...
Read more >
React-Query: You Might Not Need State Management ‍♂️
Using react-query allows us to fetch, update, caching data async very ... This hook has some very useful return values, some of them...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found