Client-Side GraphQL in React Caching & Synchronicity
Transcript from the "Caching & Synchronicity" Lesson
>> Scott Moss: So keeping cache in sync. So why is the cache out of sync in the first place? Well, if you perform a mutation that updates or creates a single node, then apollo will update your cache automatically given the mutation and query has the same fields and id. So what exactly does that mean?
[00:00:17] So basically what I'm saying is let's say this was not all pets and there was one pet. And this was not pets it was pet, I dunno, just get one pet back it's always the same pet. And let's say this was not create a pet it was update pet, and this was called update pet, okay?
[00:00:42] And let's also assume that the pet that I'm updating has the same ID as the pet that I'm getting here. When this mutation comes back, because this mutation has access to all these fields via the server side allowed us to have it, and it has the same ID as the query that I did earlier.
[00:01:00] That pet is gonna be updated first automatically because Apollo mashed them together. It's like, you have the exact same ID signature as one that I already have in my store, so I'm just gonna update you. That's happens for free. That's why that happens. Everybody understand that, all right?
[00:01:18] The IDs are the same, so therefore, it just gets replaced, and it's the same thing. You should use the same fields, because whatever reason the query needed those fields, and because whatever the mutation's gonna return is gonna replace whatever the query had? You're still gonna need those fields, so you should return the same fields.
[00:01:34] If the query didn't need those fields, it wouldn't have asked for them. So if there are some fields in there that you don't need, then you should also exclude them from your query and not just your mutation. So you should also return all of the same fields, and that's why that's important.
[00:01:47] Okay, cool.
>> Scott Moss: If you perform a mutation that updates a node in a list or removes a node, you are responsible for updating any queries referencing that list or node and there are many ways to do this with apollo. So that's our example right now. So we have a list of pets and right now we are creating a pet.
[00:02:08] That means that there is no ID in our local cache that is going to be associated with the pet that we just created. It's a whole new pet, there is no ID. So there is never going to be a point where apollo gets this back and be like there's an ID that's already in the cache, let me update it.
[00:02:24] How, we just created it, there is no ID. So it doesn't know what to do with it. And because there's no ID, there is no automatic update. The next thing is well, you might already be like well, my mutation returns a pet type, and the queries, this query also returns pets.
[00:02:41] Okay, apollo still doesn't know that you want to associate this pet that you created with this list of pets that you have over here. Like how would it know that? I mean if you did that you would have some really crazy side effects in your app that you wouldn't want to happen.
[00:02:54] To be honest, that would that would cause some issues in your app. Cuz maybe this array of pets only has these fields and they're specific for this, so this new pet that you created is not for this view, it's for this other view. If they just started adding all the pets to all the arrays, it would probably give you side effects you didn't want.
[00:03:11] You want control over that. So you have to tell apollo like yeah, I need to update my cache to add this pet here. Another example is, if you're deleting something, if I had a mutation where I was deleting something, apollo wouldn't know how to go into a list and remove it.
[00:03:27] It wouldn't know that like, you deleted this thing off the server, so for some reason I need to go into the cache and find a list of all the list thas a reference and delete it all there. It wouldn't know that, it would have no idea. So you would have to do that yourself as well.
[00:03:44] Does that make sense? Yes?
>> Speaker 2: So if I'm updating a pet that exists in all pets, apollo doesn't update?
>> Scott Moss: If you're updating one pet and it exists in all the pets, apollo will not update any pet in that list of pets.
>> Speaker 3: Even the ID's would be the same?
>> Scott Moss: Even though the IDS will be the same, because it doesn't know that you're interested in that list, right? You might have many lists of pets all throughout your app and they all are slightly different. It will not know which one of those to go update.
>> Scott Moss: Automatically, unless you gave it some hints.
[00:04:27] So automatically it wouldn't, but in some scenarios it will if you start giving it hints, and we'll talk about some of those hints.
>> Scott Moss: Any questions?
>> Speaker 4: But in this case you said when you change that to update pet it would update, right?
>> Scott Moss: No cuz I had it as one pet and one pet.
>> Speaker 4: Okay.
>> Scott Moss: Yeah so if I have multiple pets, so if I have all these pets, and then I was updating one pet with the id of a pet that was already in this array, it won't update it unless we gave it hints. And the hints in this case is literally just giving it all the fields.
[00:05:05] If you gave it all the fields, then yeah, it'll update it, yeah. In that case, yeah, if you gave it all the fields, okay, cool, because all those nodes got normalized on Flatly. And they all have IDs, so the IDs matched, and then it worked. But the fields have to be the same, otherwise you might have errors.
[00:05:20] So you have to have those fields there, otherwise, it won't work. But yes, that's only for updating, and that's only because we had it existing in the cache in the first place, yes.
>> Speaker 5: Does the order of the fields matter?
>> Scott Moss: No, orders of the fields don't matter.
[00:05:32] These are just for you, yeah. Any other questions on that?
>> Speaker 6: So if I'm just coding for I just usually sometimes I mean I don't need all of the keys, I just need maybe two keys. But while updating I'm updating all of the keys of that. Would that still update if there is like only one query, and one mutation?
[00:05:55] Would that still go and update it or?
>> Scott Moss: So what you update, as far as on the server side, what actually gets updated is irrelevant to what is happening on the front end, it's only what you send back. So, even if you updated two fields, that's fine. As long as you send back the exact fields that the query wants, that's good, and they could all be the same value, it's still gonna get replaced.
[00:06:17] So, it doesn't matter what you updated on the server, how many fields or anything like that, all of them, any of them. As long as the ID signature matches, it's gonna get replaced. So, how can we keep the cache in sync, here are a couple strategies. So the first one is we can refetch matching queries after a mutation.
[00:06:36] There are some built-in helper methods in apollo literally called refetch to fetch more, well fetch more is for pagination, but you can refetch queries after mutation. You can do it yourself using like a single way if youre not using hooks, but in our case we are using hooks so we would have to sign up to be notified when a query changes.
[00:07:00] In fact if you go look inside, where's it at. I got so many things open, there we go. If you go look inside apollo dental. My command button just doesn't work. And we click on Apollo, and we click on queries, you'll see something called watch queries. So these watch queries are called watch queries for a reason because Apollo's watching them so they can update them things happen.
[00:07:31] You can subscribe to this in the coding like I too want to watch this query, and whenever something in that query changes, you'll get a callback that you can do something. So that's how you can do a refresh if you want to like, I need to fetch more when something changes or something like that.
[00:07:44] So that's, That's one thing you can do, probably not the best way. You can also just be like after this mutation comes back, run this query again, like you could do that too. I would probably opt out of that as well. You can use the update method on the mutation, which is pretty much what we're gonna do.
[00:08:04] That's the standard approach, it's the simplest approach, it's the one that makes the most sense. If you ever use something like redux or view X or anything like that, it's basically the same thing. You're gonna be writing a reducer. The only difference is you're gonna be writing to the cache of the internal Apollo state, that's it.
[00:08:24] So you rather get direct access to the cache that we're exposing or that we're looking at here.
>> Scott Moss: This cache. You can direct access to this cache or you can write to it and you can pretty much do whatever you want with this cache. And you have to write to it yourself.
[00:08:48] But how you do it, this is where it kinda gets confusing because apollo can only write to a cache not based off on what you think. It's not a key value store where you can just be like I wanna update this pet with this ID or just push inside the pet's array, it doesn't work that way.
[00:09:05] You have to update it, you have to query the cache with graphQL and you have to write to cache with graphQL.
>> Scott Moss: Does that make sense? And reason that is is because that's how graphQL knows what has what, is through these watched queries and stuff like that. So if you wanna retrieve items from the cache, and in our example we're gonna be retrieving all the pets from all pets array.
[00:09:28] We wanna get all of those. And then we wanna add a pet to it, we gotta write it back with that same query. Question.
>> Speaker 7: Why is that a better solution as opposed to just requerying the database? Cuz what I'm understanding is that the mutation, right? The mutation updates the database so I get why the front end wouldn't reflect that, but if I manually updated the cache, isn't there more opportunity for things to go awry?
[00:09:57] Like I could mess things up there, isn't it just better to pull it straight from the source? If I updated the database, just pull from the database.
>> Scott Moss: I wouldn't say one's better or worse, it depends on if latency is a huge deal for you then having to make another API call, if it takes your API two seconds to respond for every single API call and having to make another one needs a different thing.
[00:10:21] Your users waiting two more seconds or not, versus doing something in memory that's instant. Yeah, I would say and if you set everything up right, as in your mutations return the exact same fields. You named your queries, you have reference to them through variables, updating the cache is pretty simple.
[00:10:36] You can't mess it up as long as you have queries and you have the exact fields on your mutation, it just works. It's literally the same thing as writing a reducer inside of redux. Any questions on that.
>> Speaker 8: So is there other ways to make the [INAUDIBLE] call again because if I want it to be something on demand like a click of a button on UI then?
>> Scott Moss: Yeah then you would use the client directly. So you wouldn't use the the use query, you would just say client.query, and you would hook that up to a button handler, or something like that. Don't use a hook if you want direct control over a query.