Apollo / GraphCool Optimistic UI for Delete Mutations
A lot of the examples I see online are for Optimistic UI updates for adding items to a list. I want to share another real world example of how we use Optimistic UI updates in Metacall to immediately remove Cards from the CardDrawer.
I have a removeFromDrawer
mutation that takes in drawerId
and cardsId
. So in my event handler to remove cards, I simply call the mutation like so.
handleDrawerRemoveCard(id) {
const variables = {
drawerId: this.props.drawerId,
cardId: id
}
this.props.removeFromDrawer({
variables
})
}
When I go into the CardDrawer and click on the remove button, there is a very noticeable delay before the list item is removed. Apollo Client provides a way for the client to update immediately through Optimistic UI.
In order for this to work we need to add two more options to the mutation: update
and optimisticResponse
.
Essentially, we are mocking out the response we get back from the server and updating the apollo store manually immediately instead of waiting for the response. Therefore, we add the update
field like so:
handleDrawerRemoveCard(id) {
const variables = {
drawerId: this.props.drawerId,
cardId: id
}
this.props.removeFromDrawer({
variables,
update: : (store, {data: {removeFromCardDrawer}}) => {
const data = store.readQuery({
query: drawerQuery,
variables: { drawerId: this.props.drawerId}
})
data.Drawer.cards = data.Drawer.cards.filter(card => card.id !== removeFromCardDrawer.cardsCard.id)
store.writeQuery({query: drawerQuery, data})
}
})
}
the update takes in the apollo store and the data we want to manipulate. To get the current state of the Drawer, we call the readQuery
method on the store with the proper options added on. If we console log data
and removeFromCardDrawer
we will see the below:
So all we have to do in update
is to update the Drawer.cards
Array where it will remove the card with the id from the removeFromCardDrawer.cardsCard.id
. Therefore we just filter through the cards Array where the cards do not have the id from the removeFromCardDrawer
mutation. Once we update the Array we can then write it to the store via store.writeQuery
.
However, this isn’t enough to make the experience instant. We must now add the optimisticResponse
to the mutation.
The apollo docs explains what happens with the update
and optimisticResponse
very well.
If you provide an
optimisticResponse
option to the mutation then theupdate
function will be run twice. Once immediately after you callclient.mutate
with the data fromoptimisticResponse
. After the mutation successfully executes against the server the changes made in the first call toupdate
will be rolled back andupdate
will be called with the actual data returned by the mutation and not just the optimistic response.
So in order for our handler to support Optimistic UI, we add the optimisticResponse
like this:
handleDrawerRemoveCard (id) {
const variables = {
drawerId: this.props.drawerId,
cardId: id
}
this.props.removeFromDrawer({
variables,
update: (store, {data: {removeFromCardDrawer}}) => {
const data = store.readQuery({
query: drawerQuery,
variables: { drawerId: this.props.drawerId}
})
data.Drawer.cards = data.Drawer.cards.filter(card => card.id !== removeFromCardDrawer.cardsCard.id)
store.writeQuery({query: drawerQuery, data})
},
optimisticResponse: {
removeFromCardDrawer: {
cardsCard: {
id
},
__typename: 'RemoveFromCardDrawerPayload',
},
}
})
}
Right now I am sure you are thinking how the hell do I know what the response looks like? Apollo store is built on top of Redux so I just look at the Redux DevTools in Chrome! For setup instructions and overview, click here.
When I execute the mutation, I should receive something called APOLLO_MUTATION_RESULT
. It is in this action that I can see the response in the form of data
.
Take a look at what is in my optimisticResponse
field and you’ll see that it is the exact same as what was returned from GraphCool.
And there you have it. It was a bit overwhelming for me at first but I hope I broke it down into simple and understandable steps for you to implement Optimistic UI in your own application.