mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-16 16:34:24 +02:00
refactor item list to use accessor function
This commit is contained in:
@@ -32,6 +32,13 @@ export const getListQueryKeyName = (itemType: LibraryItem): string => {
|
||||
}
|
||||
};
|
||||
|
||||
type InfiniteLoaderCacheData = {
|
||||
dataMap: Map<number, unknown>;
|
||||
idToIndexMap: Map<string, number>;
|
||||
pagesLoaded: Record<string, boolean>;
|
||||
version: number;
|
||||
};
|
||||
|
||||
interface UseItemListInfiniteLoaderProps {
|
||||
eventKey: string;
|
||||
fetchThreshold?: number;
|
||||
@@ -43,10 +50,12 @@ interface UseItemListInfiniteLoaderProps {
|
||||
serverId: string;
|
||||
}
|
||||
|
||||
function getInitialData(itemCount: number) {
|
||||
function getInitialData(): InfiniteLoaderCacheData {
|
||||
return {
|
||||
data: Array.from({ length: itemCount }, () => undefined),
|
||||
dataMap: new Map(),
|
||||
idToIndexMap: new Map(),
|
||||
pagesLoaded: {},
|
||||
version: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -118,28 +127,27 @@ export const useItemListInfiniteLoader = ({
|
||||
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
|
||||
});
|
||||
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
|
||||
// Update the query data with the fetched page
|
||||
queryClient.setQueryData(
|
||||
dataQueryKey,
|
||||
(oldData: { data: unknown[]; pagesLoaded: Record<string, boolean> }) => {
|
||||
const newData = [
|
||||
...oldData.data.slice(0, startIndex),
|
||||
...result.items,
|
||||
...oldData.data.slice(endIndex),
|
||||
];
|
||||
const newPagesLoaded = {
|
||||
...oldData.pagesLoaded,
|
||||
[pageNumber]: true,
|
||||
};
|
||||
queryClient.setQueryData(dataQueryKey, (oldData: InfiniteLoaderCacheData) => {
|
||||
const nextDataMap = new Map(oldData.dataMap);
|
||||
const nextIdToIndexMap = new Map(oldData.idToIndexMap);
|
||||
|
||||
return {
|
||||
data: newData,
|
||||
pagesLoaded: newPagesLoaded,
|
||||
};
|
||||
},
|
||||
);
|
||||
result.items.forEach((item, offset) => {
|
||||
const index = startIndex + offset;
|
||||
nextDataMap.set(index, item);
|
||||
if (item && typeof item === 'object' && 'id' in (item as any)) {
|
||||
const id = String((item as any).id);
|
||||
nextIdToIndexMap.set(id, index);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
dataMap: nextDataMap,
|
||||
idToIndexMap: nextIdToIndexMap,
|
||||
pagesLoaded: { ...oldData.pagesLoaded, [pageNumber]: true },
|
||||
version: oldData.version + 1,
|
||||
};
|
||||
});
|
||||
|
||||
// Track the last fetched page
|
||||
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
||||
@@ -179,7 +187,10 @@ export const useItemListInfiniteLoader = ({
|
||||
if (!oldData) return oldData;
|
||||
return {
|
||||
...oldData,
|
||||
dataMap: new Map(),
|
||||
idToIndexMap: new Map(),
|
||||
pagesLoaded: {},
|
||||
version: (oldData?.version ?? 0) + 1,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -211,11 +222,11 @@ export const useItemListInfiniteLoader = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dataQueryKey, queryClient, fetchPage, itemsPerPage]);
|
||||
|
||||
const { data } = useQuery<{ data: unknown[]; pagesLoaded: Record<string, boolean> }>({
|
||||
const { data } = useQuery<InfiniteLoaderCacheData>({
|
||||
enabled: false,
|
||||
initialData: getInitialData(totalItemCount),
|
||||
initialData: getInitialData(),
|
||||
queryFn: () => {
|
||||
return getInitialData(totalItemCount);
|
||||
return getInitialData();
|
||||
},
|
||||
queryKey: dataQueryKey,
|
||||
});
|
||||
@@ -233,7 +244,7 @@ export const useItemListInfiniteLoader = ({
|
||||
const pageNumber = Math.floor(range.startIndex / itemsPerPage);
|
||||
|
||||
const currentData = queryClient.getQueryData<{
|
||||
data: unknown[];
|
||||
dataMap: Map<number, unknown>;
|
||||
pagesLoaded: Record<string, boolean>;
|
||||
}>(dataQueryKey);
|
||||
|
||||
@@ -289,18 +300,20 @@ export const useItemListInfiniteLoader = ({
|
||||
|
||||
// Reset the infinite list data
|
||||
const currentData = queryClient.getQueryData<{
|
||||
data: unknown[];
|
||||
dataMap: Map<number, unknown>;
|
||||
pagesLoaded: Record<string, boolean>;
|
||||
}>(dataQueryKey);
|
||||
|
||||
if (force || currentData) {
|
||||
// Reset data to initial state and clear all loaded pages
|
||||
await queryClient.setQueryData(dataQueryKey, (oldData: any) => {
|
||||
if (!oldData) return getInitialData(totalItemCount);
|
||||
if (!oldData) return getInitialData();
|
||||
return {
|
||||
...oldData,
|
||||
data: Array.from({ length: totalItemCount }, () => undefined),
|
||||
dataMap: new Map(),
|
||||
idToIndexMap: new Map(),
|
||||
pagesLoaded: {},
|
||||
version: (oldData?.version ?? 0) + 1,
|
||||
};
|
||||
});
|
||||
lastFetchedPageRef.current = -1;
|
||||
@@ -336,28 +349,23 @@ export const useItemListInfiniteLoader = ({
|
||||
|
||||
const updateItems = useCallback(
|
||||
(indexes: number[], value: object) => {
|
||||
queryClient.setQueryData(
|
||||
dataQueryKey,
|
||||
(prev: { data: unknown[]; pagesLoaded: Record<string, boolean> }) => {
|
||||
return {
|
||||
...prev,
|
||||
data: prev.data.map((item: any, index) => {
|
||||
if (!item) {
|
||||
return item;
|
||||
}
|
||||
queryClient.setQueryData(dataQueryKey, (prev: InfiniteLoaderCacheData) => {
|
||||
const nextDataMap = new Map(prev.dataMap);
|
||||
|
||||
if (!indexes.includes(index)) {
|
||||
return item;
|
||||
}
|
||||
indexes.forEach((index) => {
|
||||
const existing = nextDataMap.get(index);
|
||||
if (!existing || typeof existing !== 'object') {
|
||||
return;
|
||||
}
|
||||
nextDataMap.set(index, { ...(existing as any), ...(value as any) });
|
||||
});
|
||||
|
||||
return {
|
||||
...item,
|
||||
...value,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
return {
|
||||
...prev,
|
||||
dataMap: nextDataMap,
|
||||
version: prev.version + 1,
|
||||
};
|
||||
});
|
||||
},
|
||||
[queryClient, dataQueryKey],
|
||||
);
|
||||
@@ -384,16 +392,9 @@ export const useItemListInfiniteLoader = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const idToIndexMap = data.data
|
||||
.filter(Boolean)
|
||||
.reduce((acc: Record<string, number>, item: any, index: number) => {
|
||||
acc[item.id] = index;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const dataIndexes = payload.id
|
||||
.map((id: string) => idToIndexMap[id])
|
||||
.filter((idx) => idx !== undefined);
|
||||
.map((id: string) => (data as any).idToIndexMap?.get(id))
|
||||
.filter((idx): idx is number => typeof idx === 'number');
|
||||
|
||||
if (dataIndexes.length === 0) {
|
||||
return;
|
||||
@@ -407,16 +408,9 @@ export const useItemListInfiniteLoader = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const idToIndexMap = data.data
|
||||
.filter(Boolean)
|
||||
.reduce((acc: Record<string, number>, item: any, index: number) => {
|
||||
acc[item.id] = index;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const dataIndexes = payload.id
|
||||
.map((id: string) => idToIndexMap[id])
|
||||
.filter((idx) => idx !== undefined);
|
||||
.map((id: string) => (data as any).idToIndexMap?.get(id))
|
||||
.filter((idx): idx is number => typeof idx === 'number');
|
||||
|
||||
if (dataIndexes.length === 0) {
|
||||
return;
|
||||
@@ -434,7 +428,40 @@ export const useItemListInfiniteLoader = ({
|
||||
};
|
||||
}, [data, eventKey, itemType, serverId, updateItems]);
|
||||
|
||||
return { data: data.data, onRangeChanged, refresh, updateItems };
|
||||
const itemCount = totalItemCount ?? 0;
|
||||
|
||||
const getItem = useCallback(
|
||||
(index: number) => {
|
||||
return (data as any).dataMap?.get(index);
|
||||
},
|
||||
[data],
|
||||
);
|
||||
|
||||
const getItemIndex = useCallback(
|
||||
(id: string) => {
|
||||
return (data as any).idToIndexMap?.get(id);
|
||||
},
|
||||
[data],
|
||||
);
|
||||
|
||||
const loadedItems = useMemo(() => {
|
||||
const map: Map<number, unknown> | undefined = (data as any).dataMap;
|
||||
if (!map || map.size === 0) return [];
|
||||
return Array.from(map.entries())
|
||||
.sort(([a], [b]) => a - b)
|
||||
.map(([, v]) => v);
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
dataVersion: (data as any).version ?? 0,
|
||||
getItem,
|
||||
getItemIndex,
|
||||
itemCount,
|
||||
loadedItems,
|
||||
onRangeChanged,
|
||||
refresh,
|
||||
updateItems,
|
||||
};
|
||||
};
|
||||
|
||||
export const parseListCountQuery = (query: any) => {
|
||||
|
||||
Reference in New Issue
Block a user