apollo icon indicating copy to clipboard operation
apollo copied to clipboard

v4: Refetch does not use typePolicies as documented in Apollo 3 docs

Open lucatk opened this issue 4 years ago • 3 comments

Describe the bug Apollo 3 recommends using InMemoryCache typePolicies for cache operations for pagination instead of updateQuery on the fetchMore function. When trying to use typePolicies with vue-apollo, the behaviour is not as intended.

To Reproduce Steps to reproduce the behavior:

  1. Create Apollo Client using an InMemoryCache which has some typePolicies for the paginated query (define merge and read functions)
const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        paginatedField: {
          keyArgs: ['filter'],
          merge (existing = makeEmptyPaginatedData(), incoming, { args: { page = 0, count = 10 } }) {
            console.log('merge options:', page, count, 'data:', incoming);
            const start = page * count;
            const merged = existing.items.slice(0);
            for (let i = 0; i < incoming.items.length; ++i) {
              merged[start + i] = incoming.items[i];
            }
            return Object.assign({}, incoming, {
              items: merged,
              index: existing.index !== -1 && existing.index < incoming.index ? existing.index : incoming.index,
              count: merged.length,
              hasPrevious: start > 0 ? existing.hasPrevious : incoming.hasPrevious,
              hasNext: start + count < merged.count ? existing.hasNext : incoming.hasNext,
            });
          },
          read (existing, { args: { page = 0, count = 10 } }) {
            if (!existing) {
              return;
            }

            const start = page * count;
            const end = start + count;
            const items = existing.items.slice(start, end);
            const result = Object.assign({}, existing, {
              items,
              index: page,
              count: items.length,
              hasPrevious: page > 0,
              hasNext: existing.items.length > end || existing.hasNext,
            });
            console.log('read options:', page, count, 'data:', result);
            return result;
          },
        },
      },
    },
  },
});
  1. Define paginated query
const { result, fetchMore } = useQuery(
  gql`query ($page: Int, $count: Int) {
    paginatedField(filter: [...], page: $page, count: $count) {
      index
      count
      hasPrevious
      hasNext
      items {
        [...]
      }
    }
  }`,
  {
    page: 0,
    count: 10,
  },
);
  1. Load page (initial query ran with variables page=0, count=10)
  2. Call fetchMore with variables page=1, count=10
  3. Console output:
merge options: 0 10 data: [...]
read options: 0 10 data: [...]
// here fetchMore is called
merge options: 1 10 data: [...]
read options: 0 10 data: [...]

Expected behavior Read function should be called with variables from most recent query (e.g. fetchMore call) just like merge function is called Console would read as follows:

merge options: 0 10 data: [...]
read options: 0 10 data: [...]
// here fetchMore is called
merge options: 1 10 data: [...]
read options: 1 10 data: [...]

Versions vue: 2.6.12 vue-apollo: @vue/[email protected] apollo-client: 3.3.18

Additional context Add any other context about the problem here.

Research guided me towards this Apollo issue, however I could not figure out how to apply this to solve my problem. #1115 also seems to be similar, but didn't provide a clear solution either.

lucatk avatar May 25 '21 09:05 lucatk

@lucatk Did you ever find a solution for this?

I found an undesirable workaround. If you wrap variables in a ref when passing to useQuery it seems to function correctly. I suspect because it is a ref, it is always taking the current value of variables.

const variables = ref({
    page: {
      limit: 20,
      offset: (parseInt((route.value.query.page as string) || 0) - 1) * 20,
      returnTotalCount: true,
    },
  })

  const { result, loading, fetchMore } = useQuery(packageQuery, variables)

watch(page, (newPage) => {
    variables.value.page.offset = (newPage - 1) * limit.value
    fetchMore({
      variables: variables.value,
    })
  })

joel-wenzel avatar Nov 22 '21 17:11 joel-wenzel

Looks like an apollo client issue to me.

Akryum avatar Nov 28 '21 17:11 Akryum

The react useQuery doesn't do anything special too

Akryum avatar Nov 28 '21 17:11 Akryum