4

I'm struggling to implement full support of infinite scroll with cursor pagination, adding new elements and removing. I have used great example from github discussion https://github.com/reduxjs/redux-toolkit/discussions/1163#discussioncomment-876186 with few adjustments based on my requirements. Here is my implementation of useInfiniteQuery.

export function useInfiniteQuery<
  ListOptions,
  ResourceType,
  ResultType extends IList<ResourceType> = IList<ResourceType>,
  Endpoint extends QueryHooks<
    QueryDefinition<any, any, any, ResultType, any>
  > = QueryHooks<QueryDefinition<any, any, any, ResultType, any>>,
>(
  endpoint: Endpoint,
  {
    options,
    getNextPageParam,
    select = defaultSelect,
    inverseAppend = false,
  }: UseInfiniteQueryOptions<ListOptions, ResultType, ResourceType>,
) {
  const nextPageRef = useRef<string | undefined>(undefined);
  const resetRef = useRef(true);
  const [pages, setPages] = useState<ResourceType[] | undefined>(undefined);
  const [trigger, result] = endpoint.useLazyQuery();

  const next = useCallback(() => {
    if (nextPageRef.current !== undefined) {
      trigger(
        { options: { ...options, page_after: nextPageRef.current } },
        true,
      );
    }
  }, [trigger, options]);

  useEffect(() => {
    resetRef.current = true;
    trigger({ options }, true);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, Object.values(options));

  useEffect(() => {
    if (!result.isSuccess) {
      return;
    }
    nextPageRef.current = getNextPageParam(result.data);
    if (resetRef.current) {
      resetRef.current = false;
      setPages(select(result.data));
    } else {
      setPages((pages) =>
        inverseAppend
          ? select(result.data).concat(pages ?? [])
          : (pages ?? []).concat(select(result.data)),
      );
    }
  }, [result.data, inverseAppend, getNextPageParam, select, result.isSuccess]);

  return {
    ...result,
    data: pages,
    isLoading: result.isFetching && pages === undefined,
    hasMore: nextPageRef.current !== undefined,
    next,
  };
}

This example works great with pagination, but the problem when I'm trying to add new elements. I can't distinguish are new elements arrived from new pagination batch or when I called mutation and invalidated tag (in this case RTK refetch current subscriptions and update result.data which I'm listening in second useEffect).

I need to somehow to identify when I need to append new arrived data (in case of next pagination) or replace fully (in case when mutation was called and needs to reset cursor and scroll to top/bottom).

My calling mutation and fetching the data placed in different components. I tried to use fixedCacheKey to listening when mutation was called and needs to reset data, but I very quickly faced with a lot of duplication and problem when I need to reuse mutation in different places, but keep the same fixed cache key.

Someone has an idea how to accomplish it? Perhaps, I need to take another direction with useInfiniteQuery implementation or provide some fixes to current implementation. But I have no idea to handle this situation. Thanks!

Kostya Zgara
  • 73
  • 1
  • 6

0 Answers0