mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
suspend infinite loader on fetch
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
|||||||
UseSuspenseQueryOptions,
|
UseSuspenseQueryOptions,
|
||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { useListContext } from '/@/renderer/context/list-context';
|
import { useListContext } from '/@/renderer/context/list-context';
|
||||||
@@ -75,6 +75,10 @@ export const useItemListInfiniteLoader = ({
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const lastFetchedPageRef = useRef<number>(-1);
|
const lastFetchedPageRef = useRef<number>(-1);
|
||||||
const currentVisibleRangeRef = useRef<null | { startIndex: number; stopIndex: number }>(null);
|
const currentVisibleRangeRef = useRef<null | { startIndex: number; stopIndex: number }>(null);
|
||||||
|
const [isRefetching, setIsRefetching] = useState(false);
|
||||||
|
const refetchPromiseRef = useRef<null | Promise<void>>(null);
|
||||||
|
const previousDataQueryKeyRef = useRef<string>('');
|
||||||
|
const isRefetchingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const { data: totalItemCount } = useSuspenseQuery<number, any, number, any>(listCountQuery);
|
const { data: totalItemCount } = useSuspenseQuery<number, any, number, any>(listCountQuery);
|
||||||
|
|
||||||
@@ -145,6 +149,15 @@ export const useItemListInfiniteLoader = ({
|
|||||||
|
|
||||||
// Reset the loaded pages and refetch current page when the query changes
|
// Reset the loaded pages and refetch current page when the query changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const currentDataQueryKey = JSON.stringify(dataQueryKey);
|
||||||
|
|
||||||
|
if (previousDataQueryKeyRef.current === currentDataQueryKey || isRefetchingRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousDataQueryKeyRef.current = currentDataQueryKey;
|
||||||
|
isRefetchingRef.current = true;
|
||||||
|
|
||||||
// Capture the current visible range before resetting
|
// Capture the current visible range before resetting
|
||||||
const visibleRange = currentVisibleRangeRef.current;
|
const visibleRange = currentVisibleRangeRef.current;
|
||||||
|
|
||||||
@@ -154,20 +167,50 @@ export const useItemListInfiniteLoader = ({
|
|||||||
pageToFetch = Math.floor(visibleRange.startIndex / itemsPerPage);
|
pageToFetch = Math.floor(visibleRange.startIndex / itemsPerPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the loaded pages
|
// Invalidate and refetch the count query to trigger Suspense
|
||||||
queryClient.setQueryData(dataQueryKey, (oldData: any) => {
|
const countQueryKey = listCountQuery.queryKey;
|
||||||
if (!oldData) return oldData;
|
|
||||||
return {
|
// Set refetching state and create a promise to suspend
|
||||||
...oldData,
|
setIsRefetching(true);
|
||||||
pagesLoaded: {},
|
const refetchPromise = (async () => {
|
||||||
};
|
try {
|
||||||
|
// Reset the loaded pages
|
||||||
|
queryClient.setQueryData(dataQueryKey, (oldData: any) => {
|
||||||
|
if (!oldData) return oldData;
|
||||||
|
return {
|
||||||
|
...oldData,
|
||||||
|
pagesLoaded: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
lastFetchedPageRef.current = -1;
|
||||||
|
currentVisibleRangeRef.current = null;
|
||||||
|
|
||||||
|
// Invalidate and wait for count query to refetch (this will suspend via useSuspenseQuery)
|
||||||
|
await queryClient.refetchQueries({
|
||||||
|
queryKey: countQueryKey,
|
||||||
|
type: 'active',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch the first page after count is refetched
|
||||||
|
await fetchPage(pageToFetch);
|
||||||
|
} finally {
|
||||||
|
setIsRefetching(false);
|
||||||
|
isRefetchingRef.current = false;
|
||||||
|
refetchPromiseRef.current = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
refetchPromiseRef.current = refetchPromise;
|
||||||
|
|
||||||
|
refetchPromise.catch(() => {
|
||||||
|
setIsRefetching(false);
|
||||||
|
isRefetchingRef.current = false;
|
||||||
|
refetchPromiseRef.current = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
lastFetchedPageRef.current = -1;
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
currentVisibleRangeRef.current = null;
|
}, [dataQueryKey, queryClient, fetchPage, itemsPerPage]);
|
||||||
|
|
||||||
fetchPage(pageToFetch);
|
|
||||||
}, [query, queryClient, dataQueryKey, fetchPage, itemsPerPage]);
|
|
||||||
|
|
||||||
const { data } = useQuery<{ data: unknown[]; pagesLoaded: Record<string, boolean> }>({
|
const { data } = useQuery<{ data: unknown[]; pagesLoaded: Record<string, boolean> }>({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -178,6 +221,11 @@ export const useItemListInfiniteLoader = ({
|
|||||||
queryKey: dataQueryKey,
|
queryKey: dataQueryKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Suspend if refetching
|
||||||
|
if (isRefetching && refetchPromiseRef.current) {
|
||||||
|
throw refetchPromiseRef.current;
|
||||||
|
}
|
||||||
|
|
||||||
const onRangeChangedBase = useCallback(
|
const onRangeChangedBase = useCallback(
|
||||||
async (range: { startIndex: number; stopIndex: number }) => {
|
async (range: { startIndex: number; stopIndex: number }) => {
|
||||||
// Track the current visible range
|
// Track the current visible range
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
import { ListKey } from '/@/renderer/store';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
interface ListContextProps {
|
interface ListContextProps {
|
||||||
customFilters?: Record<string, unknown>;
|
customFilters?: Record<string, unknown>;
|
||||||
id?: string;
|
id?: string;
|
||||||
itemCount?: number;
|
itemCount?: number;
|
||||||
pageKey: ListKey;
|
pageKey: ItemListKey | string;
|
||||||
setItemCount?: (itemCount: number) => void;
|
setItemCount?: (itemCount: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user