In previous lessons, we've looked at defining "merge" field policies, that tell Apollo how new items should be written to the cache. In this lesson, we'll look at "read" field policies, and how they can be useful for putting local client-only state in our Apollo cache, that our components can then query as if they were querying the server.
Instructor: [0:00] Let's say, we want to build a delete notes in bulk function. First, we want to give the ability to the user to select multiple notes. These simple checkboxes just log the ID of the note for now, and whether it's selected or not.
[0:15] The server doesn't actually support note selection. It doesn't support a field to tell us whether a note is selected or not. That's good. I don't really want it to support that. Which notes are selected is a state that I want to keep on the client only. I don't care about saving it to the server, or having it persist between refreshes or across devices.
[0:36] Let's see how we'd track note selection on the client only. In my NotesList component, I added this simple checkbox to each note with an onChange handler, that logs the ID of the note and the new checked state. Normally, I'd just use the useState hook to keep track locally in my component, which note IDs are selected.
[0:59] Once I have this, I would then set the checked state on my component based on whether the current note I'm looking at is part of those selected note IDs. What if other components need to know whether a note is checked or not?
[1:16] What if when I click to view a full note, I want to display in the edit note component whether that note is selected or not, and I want to keep this piece of state in sync with the state of this checkbox on the left here, because really they refer to the same note.
[1:30] If the note is selected on one place, maybe it should be selected in the other. Let's not use the useState hook because that's local to my NotesList component only. The other solution would be to just lift the state up and use the context API or the useReducer hook.
[1:47] We're already using a central data store, the Apollo cache. What if we could just ask for the is-selected state on the note? Then we'd be able to just ask each note down here whether it's selected or not.
[2:02] We already looked at using type policies to define what happens when a right occurs on a certain types field in the cache. Let's define a new policy for the note type this time. From its fields, I want to customize is-selected and define custom behavior when Apollo reads from it.
[2:24] Let's always return true for now. Let's see what happened in the browser. Oh, oh, it couldn't load the notes. If we look at the network tab at one of the failed requests, we can see that it couldn't query the is-selected field on the type note. Well, that makes sense.
[2:40] Is-selected isn't supported on the backend. It's not a field that it knows about and it's definitely not in its GraphQL schema. The server just throws an error. If we annotate it with the client flag, then we tell Apollo, don't send this field to the backend because it's a local field only. If we look in the browser again, we can see that that worked and all the notes are selected now.
[3:02] Now that selection is stored centrally on the Apollo cache, we can go to the edit note component. Instead of always hard coding false here, we can go up to where it queries the note and just ask for the local is-selected flag with the client-only annotation.
[3:19] Notice how this is a different query. It's not notes, it's note. Because Apollo knows how to normalize our notes based on their ID, it shouldn't matter what query we use. This is-selected flag should be kept in sync across notes.
[3:32] Then down here, I can set it based on the note selection status. If we open the browser fullscreen and we click to view one of the notes, cool. Now we can see that note selection is reflected in this component as well.
[3:47] In summary, we defined a new field for the note type and told Apollo that whenever it wants to read from that field to just return true instead. This then allowed us to just ask for the is-selected status on any query that deals with notes.
[4:05] Because we annotated it with the @client flag, it did not get sent to the server. This allowed us to abstract client-only state away from the components and into a central place allowing us to keep the components dumb where they can just ask the note for its selection status.
[4:22] They don't have to calculate it internally. It also allowed us to keep this state in sync between all the different components that might be looking at that note. This read policy always returns true right now, for all the notes. In the next lesson, we'll look at how to customize it per note.