All of you for once, must have found yourself in a clueless situation having spent couple of hours locating a problem in the source code which happens to be the reason behind an undesired behaviour in the application. Well, at least that’s how I found myself in the avenue of finding out why the function writeQuery() is a necessary step once we manually update the apollo cache in react, which was completely unrelated to the problem we were trying to diagnose earlier!

In our current project architecture, we’ve opted to go with Apollo GraphQL as the bridge between the backend and the frontend (React Native). Apollo is equipped with a cache which stores the result of the queries made by the user. Now, once a mutation is performed, Apollo’s cache isn’t updated in real time. For the cache to be updated with the latest version of data either the user has to reload the page or we can use the update callback provided by the Apollo’s mutation api which executes after a successful mutation.

The Wrong Way

Source Code 1

Here we’ve picked up a code block from our active mobile web app, to have a better understanding at how things work. What we have here, is an onSubmit() function which makes a mutation call with Skill IDs as the payload using the graphQLMutationHelper(). After this mutation succeeds, we need to update our local store with the update API. In the update function in ‘Source Code 1’, we’re obtaining the query from cache and updating it with the new data returned from the successful mutation.

The Apollo cache gets updated by making changes to the me variable which stores the reference of the USER_DETAILS query from the cache. By now we can see the updated data being reflected in all the places and tag this job as done. But is it so? Let’s have a look at the Apollo cache.

Fig. 1

The contents of the Apollo cache can be read using the React Developer Tools chrome extension. With the React DevTools tab open, we select the component wrapped with the withApollo HOC and in the Props section (Fig. 1) we see apolloState. This prop contains the results of all the successful queries made in the current session. In here the whole store is divided into chunks which makes it a bit of a task to find the chunk which holds the Skills (named skill_ratings).

Fig. 2

Let’s take up the first scenario, i.e., updating the cache using the reference to the data. When we look at the store, we see the user with no Skills initially (Fig. 2). Now let’s add a couple of skills to the user and run the onSubmit function (Source Code 1) we saw earlier. As per our understanding the Apollo cache should have these new skills added, but it’s contrary to our belief! What just happened here?

So, all of you must be sitting there scratching your heads to what just happened. This behaviour of the Apollo cache is quite understandable as we just updated the cache through reference but never alerted the Apollo Client about this update. Although, the data is updated the client doesn’t know about it, it doesn’t get to update the props that track the cache and neither does it update the elements that consume the store about the updates. Also, turns out if you refresh the page you get to see the result you expected to (Fig. 3) because we refreshed the store, by refetching the queries from the database.

The Immutability Principle

Before we get further, a little knowledge about the concept of immutability would only do good. Everyone in their initial days of working with React would remember one of the first things they had learned that objects shouldn’t be mutated. Ever wondered why?

In Javascript, all values except primitive values, are ‘passed by reference’. This means if you provide a complex javascript object as an argument to a function, only its ‘reference’ in the memory is passed on. When you simply change contents for the provided argument, the reference stays intact. React’s diffing algorithm assumes if a reference has not changed, the object (as well as its members) itself should not have changed. All react libraries such as Apollo, adhere to this immutability principle. Thus, in Source Code 1, any change made to the userDetails object, will not trigger a diff evaluation on the cache object.

The Right Way

Source Code 2

Use the Apollo’s writeQuery API. Simple. Here we use an updated onSubmit function (Source Code 2) which uses the writeQuery API to alert the Apollo Client about the updated query. We modify our orignal update method to call writeQuery passing on the updated cache object. This makes the Apollo Client update it’s records about the local store and in turn, alerts the consuming elements about the updates.

Fig. 3

Now the next time you’re wondering why your components aren’t getting refreshed with the updated data, evaluating your object mutability and inspecting your use of writeQuery() would be a good place to start debugging.