apollo-client: pollInterval not working with offset-based fetchMore pagination

I’m using fetchMore to implement offset-based pagination as suggested in the docs, and that works. But when I add pollInterval to refresh the data, it wipes out all the newly loaded data every time the interval triggers.

What am I doing wrong?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 4
  • Comments: 34 (14 by maintainers)

Most upvoted comments

Wouldn’t this issue actually be solvable when a poll used an updateQuery similar to fetchMore? If updateQuery had a parameter that told us if it was triggered by a poll, or a fetchMore request, we could update the prevResult with fetchMoreResult / pollResult accordingly.

I have an infinite list that uses fetchMore, but needs to be polled for items (think twitter). So basically same problem as y’all. This is how I solved it:

My API is already implementing the cursor approach where I send the id of the latest item and it’ll give me the next 10.

  1. added a param firstItemId to my api where instead of sending the next 10, it returns all newer items
  2. added a query, nested inside the original paginated query that just polls the list with firstItemId every 2 seconds
  3. use onCompleted to manually add the new items to the cache

So far it works. I can’t quite decide if it’s a clever solution or just a giant hack. Maybe it’s both 😄

@smeijer 's suggestion would 💯 fix this problem. Hope it gets implemented some time!

A little late, but one case for the fetchMore + polling is on infinite lists.

Just changing one variable doesn’t work, as the start of the list is than being removed. And polling is wished for to update information along the way.

It can for example happen that an item moves from “page 2” to “page 1” when sorting on a modifiedAt.

After reading this issue, I’m still unsure what the way to go would be here.

My solution for this:

const defaultPerPage = 10;
const poolInterval = 1000;

function SomeComponent() {
  const [currentPage, setPage] = useState(0);
  const [perPage, setPerPage] = useState(defaultPerPage);
  const [refetchTrigger, setRefetchTrigger] = useState(0);

  React.useEffect(() => {
    const interval = setInterval(() => setRefetchTrigger(Math.random()), poolInterval);
    return () => clearInterval(interval);
  }, []);

  const queryResult = useQuery(SOME_QUERY, {
    variables: { first: defaultPerPage, skip: 0 },
  });

  React.useEffect(() => {
    queryResult.fetchMore({
      variables: {
        first: perPage,
        skip: currentPage * perPage,
      },
      updateQuery: (prev, { fetchMoreResult }) => {
        return fetchMoreResult || prev;
      },
    });
  }, [queryResult.fetchMore, currentPage, perPage, refetchTrigger]);

  return (
    <div>
      <button onClick={() => setPage(currentPage + 1)}>Next</button>
      <pre>{JSON.stringify(queryResult.data, null, 2)}</pre>
    </div>
  );
}

Workaround

  // Poll interval that works with pagination
  useEffect(() => {
    const intervalId = setInterval(() => {
      const total =
        (queryResult.data?.countSessions.edges.length || 0) +
        queryResult.variables.first!;

      queryResult?.refetch({
        ...queryResult.variables,
        first: total
      });
    }, 15_000);

    return () => clearInterval(intervalId);
    // eslint-disable-next-line
  }, [
    ...Object.values(queryResult.variables).flat(),
    queryResult.data?.countSessions.pageInfo.endCursor
  ]);

Sure, but in that case you don’t get any benefit from using fetchMore to load the new page, since you’re going to reload the whole thing right afterwards anyway. I guess pagination is a misleading term - it should be something like “loading partial new data” or similar.

Sure I guess I just don’t consider this a case of pagination - since pagination is usually used to load only some data and not to reload everything. So IMO semantically pagination with polling doesn’t really make sense

@stubailo I believe that’s the approach @tmeasday was suggesting. And I really don’t know where I could’ve gotten this weird idea to use fetchMore from… 😉