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!