Skip to content

Commit 52907b9

Browse files
committed
fix: VirtualList
1 parent 029da8a commit 52907b9

File tree

2 files changed

+38
-112
lines changed

2 files changed

+38
-112
lines changed

src/ui/components/CachedVirtualList/index.tsx renamed to src/ui/components/VirtualList/index.tsx

Lines changed: 36 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,7 @@ import { Pagination } from '@/ui/components/Pagination';
66
import { useExtensionIsInTab } from '@/ui/features/browser/tabs';
77
import { LoadingOutlined } from '@ant-design/icons';
88

9-
// Generic cache structure
10-
interface DataCache<T> {
11-
[key: string]: {
12-
items: T[];
13-
total: number;
14-
timestamp: number;
15-
};
16-
}
17-
18-
// Global cache object
19-
const globalCache: Record<string, DataCache<any>> = {};
20-
21-
export interface CachedListProps<T> {
22-
// Unique namespace for this list type (e.g. 'inscriptions', 'tokens', etc.)
23-
namespace: string;
9+
export interface VirtualListProps<T> {
2410
// Current account address
2511
address: string;
2612
// Current chain type
@@ -33,8 +19,6 @@ export interface CachedListProps<T> {
3319
itemsPerRow?: number;
3420
// Initial page size
3521
pageSize?: number;
36-
// Cache expiration time in milliseconds
37-
cacheExpiration?: number;
3822
// Error handler
3923
onError?: (error: Error) => void;
4024
// Empty state message
@@ -43,26 +27,16 @@ export interface CachedListProps<T> {
4327
containerHeight?: number;
4428
}
4529

46-
// Custom event for data loaded
47-
const dispatchDataLoadedEvent = <T,>(namespace: string, items: T[]) => {
48-
const event = new CustomEvent(`cachedList:${namespace}:dataLoaded`, {
49-
detail: { items }
50-
});
51-
window.dispatchEvent(event);
52-
};
53-
54-
export function CachedList<T>({
55-
namespace,
30+
export function VirtualList<T>({
5631
address,
5732
chainType,
5833
fetchData,
5934
renderItem,
6035
itemsPerRow = 2,
6136
pageSize,
62-
cacheExpiration = 3 * 60 * 1000, // 3 minutes default
6337
onError,
6438
emptyText = 'Empty'
65-
}: CachedListProps<T>) {
39+
}: VirtualListProps<T>) {
6640
const [items, setItems] = useState<T[]>([]);
6741
const [total, setTotal] = useState(-1);
6842
const isInTab = useExtensionIsInTab();
@@ -74,6 +48,7 @@ export function CachedList<T>({
7448
const isLoadingRef = useRef(false);
7549
const containerRef = useRef<HTMLDivElement>(null);
7650
const prevChainTypeRef = useRef<string | null>(null);
51+
const chainChangedRef = useRef(false);
7752

7853
useEffect(() => {
7954
const hasViewModeChanged = prevIsInTabRef.current !== isInTab;
@@ -84,31 +59,16 @@ export function CachedList<T>({
8459

8560
if (prevPageSizeRef.current !== newPageSize) {
8661
prevPageSizeRef.current = newPageSize;
87-
8862
setPagination({ currentPage: 1, pageSize: newPageSize });
8963

90-
if (globalCache[namespace]) {
91-
Object.keys(globalCache[namespace]).forEach((key) => {
92-
delete globalCache[namespace][key];
93-
});
94-
}
95-
9664
if (address) {
9765
setIsLoading(true);
9866
isLoadingRef.current = true;
9967

10068
fetchData(address, 1, newPageSize)
10169
.then(({ list, total }) => {
102-
const cacheKey = `${address}_${chainType}_1_${newPageSize}`;
103-
globalCache[namespace][cacheKey] = {
104-
items: list,
105-
total,
106-
timestamp: Date.now()
107-
};
108-
10970
setItems(list);
11071
setTotal(total);
111-
dispatchDataLoadedEvent(namespace, list);
11272
})
11373
.catch((e) => {
11474
if (onError && e instanceof Error) {
@@ -122,62 +82,23 @@ export function CachedList<T>({
12282
}
12383
}
12484
}
125-
}, [isInTab, address, namespace, chainType, fetchData, onError, pageSize]);
126-
127-
// Initialize namespace cache if not exists
128-
if (!globalCache[namespace]) {
129-
globalCache[namespace] = {};
130-
}
131-
132-
// Get current cache key
133-
const getCacheKey = useCallback(
134-
(page: number) => {
135-
return `${address}_${chainType}_${page}_${pagination.pageSize}`;
136-
},
137-
[address, chainType, pagination.pageSize]
138-
);
139-
140-
// Check if cache is valid
141-
const isCacheValid = useCallback(
142-
(cacheEntry: { timestamp: number }) => {
143-
return Date.now() - cacheEntry.timestamp < cacheExpiration;
144-
},
145-
[cacheExpiration]
146-
);
85+
}, [isInTab, address, fetchData, onError, pageSize]);
14786

14887
const loadData = useCallback(async () => {
14988
if (!address || isLoadingRef.current) return;
15089

151-
isLoadingRef.current = true;
152-
setIsLoading(true);
153-
154-
const cacheKey = getCacheKey(pagination.currentPage);
155-
const cachedData = globalCache[namespace][cacheKey];
156-
157-
if (cachedData && isCacheValid(cachedData)) {
158-
setItems(cachedData.items);
159-
setTotal(cachedData.total);
160-
setIsLoading(false);
161-
isLoadingRef.current = false;
162-
163-
// Dispatch data loaded event
164-
dispatchDataLoadedEvent(namespace, cachedData.items);
90+
if (chainChangedRef.current) {
91+
chainChangedRef.current = false;
16592
return;
16693
}
16794

95+
isLoadingRef.current = true;
96+
setIsLoading(true);
97+
16898
try {
16999
const { list, total } = await fetchData(address, pagination.currentPage, pagination.pageSize);
170-
171-
globalCache[namespace][cacheKey] = {
172-
items: list,
173-
total,
174-
timestamp: Date.now()
175-
};
176-
177100
setItems(list);
178101
setTotal(total);
179-
180-
dispatchDataLoadedEvent(namespace, list);
181102
} catch (e) {
182103
if (onError && e instanceof Error) {
183104
onError(e);
@@ -186,42 +107,48 @@ export function CachedList<T>({
186107
setIsLoading(false);
187108
isLoadingRef.current = false;
188109
}
189-
}, [address, chainType, pagination, namespace, getCacheKey, isCacheValid, fetchData, onError]);
110+
}, [address, pagination, fetchData, onError]);
190111

191-
// Clear cache when account or chain type changes
192112
useEffect(() => {
193-
if (prevChainTypeRef.current !== null && prevChainTypeRef.current !== chainType) {
113+
if (prevChainTypeRef.current !== chainType) {
114+
chainChangedRef.current = true;
115+
194116
setPagination({ currentPage: 1, pageSize: effectivePageSize });
195117
setItems([]);
196118
setTotal(-1);
197119
if (containerRef.current) {
198120
containerRef.current.scrollTop = 0;
199121
}
200122

201-
Object.keys(globalCache[namespace]).forEach((key) => {
202-
delete globalCache[namespace][key];
203-
});
204-
205-
fetchData(address, 1, effectivePageSize)
206-
.then(({ list, total }) => {
207-
setItems(list);
208-
setTotal(total);
209-
})
210-
.catch((e) => {
211-
if (onError && e instanceof Error) {
212-
onError(e);
213-
}
214-
});
123+
if (!isLoadingRef.current && address) {
124+
isLoadingRef.current = true;
125+
setIsLoading(true);
126+
127+
fetchData(address, 1, effectivePageSize)
128+
.then(({ list, total }) => {
129+
setItems(list);
130+
setTotal(total);
131+
})
132+
.catch((e) => {
133+
if (onError && e instanceof Error) {
134+
onError(e);
135+
}
136+
})
137+
.finally(() => {
138+
setIsLoading(false);
139+
isLoadingRef.current = false;
140+
chainChangedRef.current = false;
141+
});
142+
}
215143
}
216144

217145
prevChainTypeRef.current = chainType;
218-
}, [chainType, address, namespace, effectivePageSize, fetchData, onError]);
146+
}, [chainType, address, effectivePageSize, fetchData, onError]);
219147

220148
useEffect(() => {
221149
loadData();
222-
}, [pagination, address, loadData, chainType]);
150+
}, [pagination, address, loadData]);
223151

224-
// Create grid rows
225152
const gridRows = useMemo(() => {
226153
const rows: T[][] = [];
227154
for (let i = 0; i < items.length; i += itemsPerRow) {

src/ui/pages/Main/WalletTabScreen/InscriptionList.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useState } from 'react';
22

33
import { Inscription } from '@/shared/types';
44
import { useTools } from '@/ui/components/ActionComponent';
5-
import { CachedList } from '@/ui/components/CachedVirtualList';
65
import InscriptionPreview from '@/ui/components/InscriptionPreview';
6+
import { VirtualList } from '@/ui/components/VirtualList';
77
import { useExtensionIsInTab } from '@/ui/features/browser/tabs';
88
import { useCurrentAccount } from '@/ui/state/accounts/hooks';
99
import { useChainType } from '@/ui/state/settings/hooks';
@@ -74,8 +74,7 @@ export function InscriptionList() {
7474
const itemsPerRow = isInTab && !isMobile ? 9 : 2;
7575

7676
return (
77-
<CachedList<Inscription>
78-
namespace="inscriptions"
77+
<VirtualList<Inscription>
7978
address={currentAccount.address}
8079
chainType={chainType}
8180
fetchData={fetchInscriptions}

0 commit comments

Comments
 (0)