Apollo-client: partialRefetch not triggering a refetch after writing a partial item to the cache

Created on 18 Sep 2019  路  3Comments  路  Source: apollographql/apollo-client

Intended outcome:
Updating the cache with a call to writeQuery to add a new item to the cache, but the new item is _missing some fields_ that are required by another query (which is using partialRefetch: true). It is expected that, given the query can no longer be satisfied by the cache, a refetch would be triggered.

Actual outcome:
No refetch is triggered, despite the cache only having a partial result for the query, resulting in a component that is out of sync with the cache.

How to reproduce the issue:
This is a contrived example that demonstrates the problem:

const orgsQuery = gql`
  query {
    member(id: "me") @client {
      id
      organizations {
        id
        # NOTE we have a 'name' here that is _not_ included when we do the writeQuery later.
        # Having this 'name' field omitted from the writeQuery is what I would _expect_ to trigger
        # a refetch of the component query, due to partial data
        name
      }
    }
  }
`;

const MemberStats: FunctionComponent = () => {
  const { data, loading, error } = useQuery(orgsQuery, {
    partialRefetch: true,
  });
  // This is not hit after we invoked the writeQuery
  console.log('hook results', { data, loading, error });

  const client = useApolloClient();
  const onButtonClick = () => {
    // This data is simulating a new organization being added to a member, but the new
    // entry _only_ has an ID (it does not contain a name)
    const newData = {
      member: {
        __typename: 'Member',
        id: data.member.id,
        organizations: data.member.organizations
          .map((organization: { id: string }) => ({
            id: organization.id,
            __typename: 'Organization',
          }))
          .concat({
            id: 'some-new-organization',
            __typename: 'Organization',
          }),
      },
    };

    const queryToWrite = gql`
      query {
        member(id: "me") @client {
          id
          organizations {
            id
          }
        }
      }
    `;
    console.log('writing new data', { newData });
    client.writeQuery({ query: queryToWrite, data: newData });

    // Examining the cache shows that the new Organization was successfully added,
    // and associated with the member
    console.log('cache after write', { cache: client.extract() });
  };

  if (!data || loading || error) {
    return null;
  }

  return (
    <div>
      {data.member.organizations.map(
        (organization: { name: string; id: string }) => (
          <div key={organization.id}>{organization.name}</div>
        ),
      )}
      <button onClick={onButtonClick}>OI</button>
    </div>
  );
};

At a high level we are:

  1. Rendering a component, with a query that asks for the id and name fields on the organizations belonging to the member
  2. Using writeQuery to add a _new_ organization to the member, but _only_ including the id field (no name field)

Most helpful comment

Note: by adding returnPartialData: true the hook _does_ trigger a re-render with the partial organization:

image

But this is not what I want (I want complete data, which should be hydrated by refetching the query)

A re-render is _also_ triggered if we _do_ include the 'name' of the new organization in writeQuery (regardless of whether returnPartialData or partialRefetch are set to true)

All 3 comments

Note: by adding returnPartialData: true the hook _does_ trigger a re-render with the partial organization:

image

But this is not what I want (I want complete data, which should be hydrated by refetching the query)

A re-render is _also_ triggered if we _do_ include the 'name' of the new organization in writeQuery (regardless of whether returnPartialData or partialRefetch are set to true)

Was this page helpful?
0 / 5 - 0 ratings