Pagination with Apollo and React

In order to load only some register of a list, and load the next ones with a button “load more”, one of those two examples could be implemented:

example of the query on Playground:

The first option, we can define inside the InMemoryCache:

export const client = new ApolloClient({
  uri: 'http://localhost:3333/graphql',
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          someKbRegisters: { //Query name
            keyArgs: false,
            // Concatenate prev with new
            merge(existing = [], incoming) {
              return [...existing, ...incoming];
            }
          }
        }
      }
    }
  }),
});

With this implementation, when we call the fetchMore function, Apollo client will merge the result. Below an Example page to show the result:

import { useQuery } from '@apollo/client';
import { Button, List, ListItem, CircularProgress } from '@material-ui/core';
import { useState } from 'react';

import { SOME_REGISTERS } from '../../services';

export default function ListRegisters() {
  const [offset, setOffset] = useState(2);

  const { data, fetchMore, error } = useQuery(SOME_REGISTERS, {
    variables: {
      offset: 0,
      limit: 2
    },
    notifyOnNetworkStatusChange: true,
  });

  const handleMore = () => {
    const nextOffset = offset + 2;
    setOffset(nextOffset);

    fetchMore({
      variables: {
        offset,
        limit: 2
      },
    });
  };

  if (error) return (<>{`Error! ${error.message}`}</>);

  // Very important, do not use loading, because it will load the entire list again.
  // if (loading) return (<>Loading...</>);
  if (!data || !data.someKbRegisters) {
    return <CircularProgress />;
  }

  return(
    <>
      {data.someKbRegisters.map((register: any) => {
        return (
          <List>
            <ListItem key={register.id} >{register.title}</ListItem>
          </List>
        )
      })}
        <Button 
          type="button"
          onClick={() => handleMore()}
          variant="contained" >
          Carregar mais
        </Button>
    </>
  );
}

Now, the second option. Forget about the typePolicies configuration merge. Just use the InMemoryCache as default:

export const client = new ApolloClient({
  uri: 'http://localhost:3333/graphql',
  cache: new InMemoryCache({}),
});

And use the handleMore function like this:

const handleMore = () => {
    const nextOffset = offset + 2;
    setOffset(nextOffset);

    fetchMore({
      variables: {
        offset,
        limit: 2
      },
      // This part makes the same of the configures in services.ts
      // -->
      updateQuery: (prevResult, { fetchMoreResult }) => {
        if (!fetchMoreResult) { 
          return prevResult; 
        }
        return {
          ...prevResult,
          someKbRegisters: [
            ...prevResult.someKbRegisters,
            ...fetchMoreResult.someKbRegisters,
          ]
        }
      },
      // <--
    });
  };
Pagination with Apollo and React

Leave a Reply

Your email address will not be published. Required fields are marked *