Apollo Client to update data from GraphQL

Dilip Kumar
4 min readAug 8, 2018

Apollo Client 2.0 has Mutation as React component to update data from GraphQL. The Mutation component is what we will use to trigger mutations from your UI.

import gql from "graphql-tag";
import { Mutation } from "react-apollo";

const ADD_TODO = gql`
mutation AddTodo($type: String!) {
addTodo(type: $type) {
id
type
}
}
`;

const AddTodo = () => {
let input;

return (
<Mutation mutation={ADD_TODO}>
{(mutation, { data }) => (
<div>
<form
onSubmit={e => {
e.preventDefault();
mutation({ variables: { type: input.value } });
input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Add Todo</button>
</form>
</div>
)}
</Mutation>
);
};

The mutate function optionally takes following

  1. variables
  2. optimisticResponse
  3. refetchQueries
  4. update

Note: We can also pass in those values as props to the Mutation component.

Use of refetchQueries to updating the cache

refetchQueries is the simplest way of updating the cache. With refetchQueries you can specify one or more queries that you want to run after a mutation is completed in order to refetch the parts of the store that may have been affected by the mutation:

import { Mutation } from 'react-apollo';
import AddSellerFormForAdmin from 'app-dumb-components/dist/AddSellerFormForAdmin';
import { ADD_UPDATE_SELLER_DETAILS } from 'app-smart-components/dist/graphql';
class AddOrEditSellerFormForAdmin extends React.Component {
state = { message: null };
render() {
return (
<Mutation mutation={ADD_UPDATE_SELLER_DETAILS}>
{ mutation => (
<AddSellerFormForAdmin
onSubmit={payload => this.addOrUpdateSeller(mutation, payload)}
seller={this.props.seller}
message={this.state.message} />
) }
</Mutation>
);
}
}
addOrUpdateSeller = async (mutation, payload) => {
const sellerId = this.props.seller.sellerId;
const updatedPayload = {
sellerId,
name: payload.name,
};
await mutation({ // Make API call to save seller details
mutation: ADD_UPDATE_SELLER_DETAILS,
variables: { payload: [updatedPayload] },
refetchQueries: [ADD_UPDATE_SELLER_DETAILS]
});

Use of update function to updating the cache

Sometime, Apollo cache become out of sync when we perform update as per data that is already in the cache. For example

  1. Deleting item from list
  2. Adding items to a list
  3. Updating item from a list

We need a way to tell Apollo Client to update the query for the list of items. This is where the update function comes in!

  • The update function is called with the Apollo cache as the first argument. The cache has several utility functions such as
  1. cache.readQuery
  2. cache.writeQuery
  • The second argument to the update function is an object with a data property containing your mutation result.

Note: If you specify an optimistic response, your update function will be called twice: once with your optimistic result, and another time with your actual result.

import { Mutation } from 'react-apollo';
import AddSellerFormForAdmin from 'app-dumb-components/dist/AddSellerFormForAdmin';
import { ADD_UPDATE_SELLER_DETAILS } from 'app-smart-components/dist/graphql';
class AddOrEditSellerFormForAdmin extends React.Component {
state = { message: null };
render() {
return (
<Mutation mutation={ADD_UPDATE_SELLER_DETAILS}>
{ mutation => (
<AddSellerFormForAdmin
onSubmit={payload => this.addOrUpdateSeller(mutation, payload)}
seller={this.props.seller}
message={this.state.message} />
) }
</Mutation>
);
}
}
addOrUpdateSeller = async (mutation, payload) => {
const sellerId = this.props.seller.sellerId;
const updatedPayload = {
sellerId,
name: payload.name,
};
await mutation({ // Make API call to save seller details
mutation: ADD_UPDATE_SELLER_DETAILS,
variables: { payload: [updatedPayload] },
update: (store, { data }) => {
const existingData = store.readQuery({ query: GET_SELLER_DETAILS, variables: { sellerUserIds: [] } }); // Read the data from the cache
const [sellerObj] = data.addUpdateSellers.sellers;
existingData.getSellers.sellers.unshift(sellerObj); // Add in case of add seller
store.writeQuery({ query: GET_SELLER_DETAILS, variables: { sellerUserIds: [] }, data: existingData }); // Write the data back to the cache.
},
});

Note: Not every mutation requires an update function. If you’re updating a single item, you usually don’t need an update function as long as you return the item’s id and the property you updated. Following is sample example.

const UPDATE_TODO = gql`
mutation UpdateTodo($id: String!, $type: String!) {
updateTodo(id: $id, type: $type) {
id
type
}
}
`;

const Todos = () => (
<Query query={GET_TODOS}>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;

return data.todos.map(({ id, type }) => {
let input;

return (
<Mutation mutation={UPDATE_TODO} key={id}>
{updateTodo => (
<div>
<p>{type}</p>
<form
onSubmit={e => {
e.preventDefault();
updateTodo({ variables: { id, type: input.value } });

input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Update Todo</button>
</form>
</div>
)}
</Mutation>
);
});
}}
</Query>
);

Optimistic UI

It improve perceived performance by returning an optimistic response before your mutation result comes back from the server.

addOrUpdateSeller = async (mutation, payload) => {
const sellerId = this.props.seller.sellerId;
const updatedPayload = {
sellerId,
name: payload.name,
};
await mutation({ // Make API call to save seller details
mutation: ADD_UPDATE_SELLER_DETAILS,
variables: { payload: [updatedPayload] },
optimisticResponse: {
addUpdateSellers: {
sellers: [{
...updatedPayload,
sellerId: sellerId || Math.round(Math.random() * -1000000),
__typename: 'Seller',
}],
},
},
update: (store, { data }) => {
const existingData = store.readQuery({ query: GET_SELLER_DETAILS, variables: { sellerUserIds: [] } }); // Read the data from the cache
const [sellerObj] = data.addUpdateSellers.sellers;
existingData.getSellers.sellers.unshift(sellerObj); // Add in case of add seller
store.writeQuery({ query: GET_SELLER_DETAILS, variables: { sellerUserIds: [] }, data: existingData }); // Write the data back to the cache.
},
});

--

--