Search code examples
reactjscachinggraphqlapollo-client

Graphql - Apollo Client/ React - Cache - fetchMore doesn't update the data state of the query


I try to implement cached pagination, in my react app using apollo client.

my query has filter argument, which should be the only argument that create a new key in the cache object.

for some reason, when fetchMore occurs with filter specified, the new data doesn't cause a re-render in the component.

I logged the existing and incoming argument in the merge function, and it seems that for each fetchMore that had filter, new data did arrive. so, i don't understand why the component didn't re-render.

to make things worst: calling fetchMore several times with or without filter send http request and merging the incoming data with the existing data. which i'd expect wouldn't happen as the client should see that it already has a key in the cache for that query with that key argument.

the following is the query:

query Shells($first: Int = 5, $offset: Int = 0, $filter: ShellFilter) {
  shells(
    orderBy: [STATUS_ASC, EXECUTION_FROM_DESC]
    first: $first
    offset: $offset
    filter: $filter
  ) {
    nodes {
      ...ShellData
    }
    totalCount
  }
}

the apolloClient config is like this:

const client = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          shells: {
            keyArgs: ['filter'],
            merge: (existing, incoming) => {
              console.log('existing:', existing, 'incoming:', incoming);
              return mergeObjectsAndNestedArrays<ShellsConnection>(
                existing,
                incoming,
                'nodes',
              );
            },
          },
        },
      },
    },
  })

and the component that displays it:

const ControlCenter = () => {
  const { showModal } = useModalContext();
  const [page, setPage] = useState(1);
  const { data, loading, fetchMore } = useShellsQuery();
  const [query, setQuery] = useURLQuery();

  const onCounterpartiesChange = async (counterparties) => {
    await fetchMore({
      variables: {
        filter: { shellCounterParties: { some: { orgId: { in: '20584' } } } },
      },
    });
    setQuery({ counterparties });
  };

  const shells = data?.shells?.nodes;

  console.log('hello from shells:', shells);

these are the logs:

enter image description here

EDIT 1 - docs reference

Following the docs: https://www.apollographql.com/docs/react/pagination/key-args/#setting-keyargs

any argument can be used as the keyArgs: limit, offset and filter.


Solution

  • In the documentation examples, the arg used as the key is a primitive value, but in your case, the filter arg is an object. This could be causing apollo to see all results as the same cached version. If your data depend only on the orgID I think you could try the nested array notation to set that field as the key.

    keyArgs: ["filter", ["shellCounterParties", ["some", ["orgId", ["in"]]]]]
    

    or the custom function

    keyArgs: (args, context) => args.filter.shellCounterParties.some.orgId.in
    

    If you really need to cache according to the whole filter object, I guess the simplest way would be stringifying it

    keyArgs: (args, context) => JSON.stringify(args.filter)
    

    But to be sure how apollo is caching the data, I highly recommend you to try the apollo devtools

    related: https://github.com/apollographql/apollo-client/issues/7314