Refetch results in Apollo Client after fetchMore?

Viewed 145

I have a query where you can fetch more results as you scroll:

const PAGE_SIZE = 10;

const [page, setPage] = useState(0);

const { data, loading, fetchMore } = useQuery(MY_QUERY, {
   variables: { limit: PAGE_SIZE, offset: 0 },
});

const handleFetchMore = () => {
  fetchMore({
    variables: { limit: PAGE_SIZE, offset: (page + 1) * PAGE_SIZE },
  });
  setPage(prev => prev + 1);
}

This is working however the default behaviour of Apollo is not to refetch when there is cached data. Every time the user goes to this page I want to initially show the cached results and check for new results in the background, and update the UI if there are new results.

I could refetch in a useEffect hook however if the user has previously called handleFetchMore then I believe the query will be called with the offset variable being more than 0. This means that a new result which should be first will not be shown.

const PAGE_SIZE = 10;

const [page, setPage] = useState(0);

const { data, loading, fetchMore, refetch } = useQuery(MY_QUERY, {
   variables: { limit: PAGE_SIZE, offset: 0 },
});

const handleFetchMore = () => {
  fetchMore({
    variables: { limit: PAGE_SIZE, offset: (page + 1) * PAGE_SIZE },
  });
  setPage(prev => prev + 1);
}

useEffect(()=>{
  if(!loading) {
    refetch()
  }
},[])

I could always call refetch with the offset as 0. However then you lose the extra results which were previously returned from fetchMore, which isnt great UX.

const PAGE_SIZE = 10;

const [page, setPage] = useState(0);

const { data, loading, fetchMore, refetch } = useQuery(MY_QUERY, {
   variables: { limit: PAGE_SIZE, offset: 0 },
});

const handleFetchMore = () => {
  fetchMore({
    variables: { limit: PAGE_SIZE, offset: (page + 1) * PAGE_SIZE },
  });
  setPage(prev => prev + 1);
}

useEffect(()=>{
  if(!loading) {
    refetch({ limit: PAGE_SIZE, offset: 0 });
  }
},[])

This must be a common scenario so is there a best practice approach?

1 Answers

This is how that process is described in Apollo docs:

const FEED_QUERY = gql`
  query Feed($type: FeedType!, $offset: Int, $limit: Int) {
    currentUser {
      login
    }
    feed(type: $type, offset: $offset, limit: $limit) {
      id
      # ...
    }
  }
`;

const FeedWithData = ({ match }) => (
  <Query
    query={FEED_QUERY}
    variables={{
      type: match.params.type.toUpperCase() || "TOP",
      offset: 0,
      limit: 10
    }}
    fetchPolicy="cache-and-network"
  >
    {({ data, fetchMore }) => (
      <Feed
        entries={data.feed || []}
        onLoadMore={() =>
          fetchMore({
            variables: {
              offset: data.feed.length
            },
            updateQuery: (prev, { fetchMoreResult }) => {
              if (!fetchMoreResult) return prev;
              return Object.assign({}, prev, {
                feed: [...prev.feed, ...fetchMoreResult.feed]
              });
            }
          })
        }
      />
    )}
  </Query>
);

Need to use a fetchPolicy="cache-and-network" and updateQuery function accumulates cached results with new ones.

Related