Mutation and update cache
Now let's do the integration part. Open src/components/Todo/TodoItem.tsx
and add the following code below the other imports:
import * as React from 'react';+ import { gql } from "@apollo/client";import { Todos } from '../../generated/graphql';
Let's define the graphql mutation to update the completed status of the todo
src/components/Todo/TodoItem.tsxinterface TodoItemType {index: number,todo: Pick<Todos, "id" | "title" | "is_completed">};+ const TOGGLE_TODO = gql`+ mutation toggleTodo ($id: Int!, $isCompleted: Boolean!) {+ update_todos(where: {id: {_eq: $id}}, _set: {is_completed: $isCompleted}) {+ affected_rows+ returning {+ id+ title+ is_completed+ }+ }+ }+ `;
Apollo useMutation Hook
We need to use the useMutation
hook to make the mutation. So let's import it.
import * as React from 'react';import { gql } from "@apollo/client";+ import { gql, useMutation } from "@apollo/client";import { Todos } from '../../generated/graphql';
And now, let's use the hook inside the TodoItem component.
const TodoItem = ({index, todo}: TodoItemType) => {+ const [todoUpdate] = useMutation(TOGGLE_TODO);
We already have the onChange handler toggleTodo for the input. Let's update the function to trigger the mutation. As we saw earlier, the mutate
function takes optional arguments like variables and update. In addition, it also accepts optimisticResponse
to update the UI before the actual result arrives.
const toggleTodo = () => {+ todoUpdate({+ variables: { id: todo.id, isCompleted: !todo.is_completed },+ optimisticResponse: {+ __typename: "mutation_root",+ update_todos: {+ __typename: "todos_mutation_response",+ affected_rows: 1,+ returning: [+ {+ __typename: "todos",+ id: todo.id,+ title: todo.title,+ is_completed: !todo.is_completed+ }+ ]+ }+ }+ });};
The above code will just make a mutation, updating the todo's is_completed property in the database.
To update the cache, we will be using the update
function again to modify the cache. We need to fetch the current list of todos from the cache before modifying it. So let's import the query.
import * as React from 'react';import { gql, useMutation } from "@apollo/client";+ import { GET_MY_TODOS } from './TodoPrivateList';
Now let's add the code for update
function.
const TodoItem = ({index, todo}: TodoItemType) => {- const [todoUpdate] = useMutation(TOGGLE_TODO);+ const [todoUpdate] = useMutation(+ TOGGLE_TODO,+ {+ update(cache, { data }) {+ const existingTodos : any = cache.readQuery({ query: GET_MY_TODOS });+ const newTodos = existingTodos!.todos.map((t:any) => {+ if (t.id === todo.id) {+ return { ...t, ...data!.update_todos!.returning[0] };+ } else {+ return t;+ }+ });+ cache.writeQuery({+ query: GET_MY_TODOS,+ data: {todos: newTodos}+ });+ }+ }+ );
We are fetching the existing todos from the cache using cache.readQuery
and updating the is_completed value for the todo that has been updated.
Finally we are writing the updated todo list to the cache using cache.writeQuery
.
Mapping Types
Now that the update mutation is completed, let's add type safety. We need type definitions for the the cache update portions.
import { GET_MY_TODOS } from './TodoPrivateList';- import { Todos } from '../../generated/graphql';+ import { GetMyTodosQuery, Todos, ToggleTodoMutation, ToggleTodoMutationVariables } from '../../generated/graphql';
Now let's add it to both readQuery and writeQuery.
- const [todoUpdate] = useMutation(+ const [todoUpdate] = useMutation<ToggleTodoMutation, ToggleTodoMutationVariables>(TOGGLE_TODO,{update(cache, { data }) {const existingTodos = cache.readQuery({ query: GET_MY_TODOS });+ const existingTodos = cache.readQuery<GetMyTodosQuery>({ query: GET_MY_TODOS });const newTodos = existingTodos!.todos.map(t => {if (t.id === todo.id) {return { ...t, ...data!.update_todos!.returning[0] };} else {return t;}});- cache.writeQuery({+ cache.writeQuery<GetMyTodosQuery>({query: GET_MY_TODOS,data: {todos: newTodos}});}});
- Build apps and APIs 10x faster
- Built-in authorization and caching
- 8x more performant than hand-rolled APIs