In this lesson, we'll look at how to use the cache.modify() utility to manually reach into the Apollo cache and update cached queries. Once a mutation has finished, Apollo will invoke the callback you pass to its "update" option, with any data the mutation returned. We'll use the returned note and the cache.identify() utility to filter out the delete note reference from our notes list inside our cache.modify() invocation.
Instructor: [0:00] the UI works now. When I press Delete, the note disappears from the list, but it's making a network call, a network call that has to travel across the Internet. We have to wait for it to come back before we can reflect the changes in the UI.
[0:13] It's not the cheapest call. Every time I delete a note, I am re-fetching all the available notes of a specific category on my backend, each with their own content. If we could somehow reach into the cache and delete a note reference directly on this query, our NotesList would then update to reflect that, without us having to make a separate, expensive network call.
[0:39] Refetch queries is still a very good, simple-to-implement choice for a lot of use cases. In this case, I'll remove it to see if we can do better. The useMutation hook allows us, via its update option, to give it a callback that will be invoked whenever this mutation finishes. It's going to be called with two items, a reference to my Apollo cache and my mutation result.
[1:03] Let's log the result and see this in action. When I press Delete, I get my update function called. I can see the result has a data property that matches my mutation query. It has a delete note on it and my successful flag. Cool.
[1:18] That means whatever I ask for in the mutation comes back on here. Now that we have a reference to the cache, I can modify it. This is a very versatile function that allows us to specify any ID of any object in our cache, let's say my first note.
[1:36] Remember how Apollo keys objects in the cache by their type and their ID on the backend. Then via our fields property, I can specify how I want individual fields of this object to be modified.
[1:50] For example, if I want to modify the content property of my first note, I can give it a callback that receives the existing content, and I can return some new modified content from it. Let's try this out. By the way, you might see this first note keep popping up occasionally.
[2:07] That's because I'm resetting my server in the background while we're playing around with these DELETE functions. Now if I press Delete on the first note, I can see my edit appear here.
[2:19] What I'd really like to modify is, instead of these individual notes here, I'd like to modify my top-level notes query. See how it's under the root queries. That means it's not a field contained in any object. It lives at the top of the cache.
[2:38] If we want to modify top-level queries, we can skip the ID completely as that means we're going to look at the root. My list of notes will be just a field on the root.
[2:51] This callback will be invoked with the existing list of notes. Let's log what the existing notes look like. I'm just going to return them unchanged, for now. If I refresh my page and I click to delete one of the notes, I'll get an array.
[3:07] If we open the array, I'll see all of my note references exactly how they were presented in the devtools, as well. To work with this list, I need a simple list of note IDs. Whenever you see this many underscores in something, it usually means it's a very internal private property of a library. You don't want to use directly as it might change and break in the future.
[3:31] To turn this list of references into simple IDs, I'll map over them. I'll use a utility function on my cache called identify, and pass it to my note reference. The identify function takes a reference to an object, generates an ID from it, and unlock my note IDs instead.
[3:50] Back in my browser, if I press Delete on a note and I look at the generated note IDs, I can see the IDs directly as I expected them. Now, I can use this technique to instead of returning all the existing nodes, I'll filter them and keep only the ones whose ID is different from I deleted note ID.
[4:11] Where am I going to get this from? I'll remove all this logging. If we go up to our mutation, remember how from the last lesson it allows us to return the confirmed ID of the deleted note. I can then assume that my deleted note ID will be on my mutationresult.data.deletenote, because remember, it matches the shape of my query, that note.ID.
[4:36] The problem is, this ID will be the direct ID of my note on the back end, as in one or two, while here, we'll be comparing a full Apollo cash IDs like note 1 and note 2. While we could add a note string in front of this, it turns out our identify function cannot only generate IDs from object refs, but from full objects as well, such as my note here.
[5:04] I'm just going to wrap my deleted note in it. Now, if I switched my browser and I click Delete, the note disappears, but I only get the network call from my mutation. I don't get the follow-up call to get the new list of notes. Modifying our local cache worked. That's cool.
[5:22] In summary, we used Apollo's mutation update callback to execute some logic after our mutation finished.
[5:31] The cache.modify utility allows us to not only directly modify fields of specific objects in the cache, but also top-level queries, such as notes. We then use the cash.identify function to convert an internal node reference to a string ID, and compare that with the ID of the note that was deleted. Cache.identify also works with full objects, as long as they have an ID and a type name.