reactjsgraphqlapolloreact-apollooptimistic-ui

React-Apollo: Query with variables causes the UI to no longer be optimistic


I'm writing a React component using Apollo GraphQL (with React Apollo Hooks, but this makes no difference).

Here is my query:

const { data, error, loading } = useQuery(ListContacts, {
  fetchPolicy: 'cache-and-network'
  // variables: { limit: 1000 }
});

And here is my function that does the mutation:

const createContact = useMutation(gql(mutations.createContact));
const deleteContact = useMutation(gql(mutations.deleteContact));
const updateContact = useMutation(gql(mutations.updateContact));

async function handleContactSave(contact) {
  const isEmpty = allStringFieldsAreEmptyFromContact(contact);
  const { id, ...processedContact } = replaceEmptyStringsWithNull(contact);
  const idIsInArray = includesContactWithId(id)(contacts);

  try {
    if (isEmpty) {
      if (idIsInArray) {
        await deleteContact({
          variables: { input: { id } },
          optimisticResponse: () => ({ deleteContact: { id } }),
          update: (cache, { data: { deleteContact } }) => {
            const query = ListContacts;
            const data = cache.readQuery({ query });
            data.listContacts.items = data.listContacts.items.filter(
              item => item.id !== deleteContact.id
            );
            cache.writeQuery({ query, data });
          }
        });
      }
    } else {
      if (idIsInArray) {
        await updateContact({
          variables: { input: { id, ...processedContact } },
          optimisticResponse: () => ({
            updateContact: { __typename: 'Contact', id, ...processedContact }
          }),
          update: (cache, { data: { updateContact } }) => {
            const query = ListContacts;
            const data = cache.readQuery({ query });
            data.listContacts.items = [
              ...data.listContacts.items.filter(
                item => item.id !== updateContact.id
              ),
              updateContact
            ];
            cache.writeQuery({ query, data });
          }
        });
      } else {
        await createContact({
          variables: { input: { ...processedContact } },
          optimisticResponse: () => ({
            createContact: {
              __typename: 'Contact',
              id: uuid(),
              ...processedContact
            }
          }),
          update: (cache, { data: { createContact } }) => {
            const query = ListContacts;
            const data = cache.readQuery({ query });
            data.listContacts.items = [
              ...data.listContacts.items.filter(
                item => item.id !== createContact.id
              ),
              createContact
            ];
            cache.writeQuery({ query, data });
          }
        });
      }
    }
  } catch (error) {
    console.log(error);
  }
}

As soon as I comment out the variables key with the limit the UI does not update optimistically any more. With the value commented out it does. What is going wrong here?


Solution

  • The writeQuery method also accepts a variables value. Every combination of query and variables is treated as a separate query by Apollo. So if one Query component (or hook) uses a limit of 100 and another uses a limit of 50, when writing your update function, you'll need to call writeQuery for both of these queries, passing in a different value for variables each time:

    cache.writeQuery({ query, data, variables: { limit: 100 } })
    cache.writeQuery({ query, data, variables: { limit: 50 } })
    

    This is a know limitation of the client. In effect, you need to keep track of the variables through local state in order to correctly call writeQuery when your update function is called. This can be, understandably, a huge hassle. Luckily, you can utilize apollo-link-watched-mutation to get around the issue.