Skip to content

Commit 2383029

Browse files
committed
changes
1 parent f8eab42 commit 2383029

1 file changed

Lines changed: 93 additions & 3 deletions

File tree

Dechat/dex_with_fiat_frontend/src/hooks/useBeneficiaries.ts

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,74 @@ export interface Beneficiary {
2424

2525
const STORAGE_KEY = 'stellar_beneficiaries';
2626

27+
/**
28+
* Request deduplication system to prevent duplicate API calls.
29+
* Concurrent fetches for the same key share one in-flight promise,
30+
* with the result distributed to all waiters.
31+
*/
32+
class RequestDeduplicator {
33+
private inFlightRequests = new Map<string, Promise<Beneficiary[]>>();
34+
35+
/**
36+
* Get or create a promise for the given key.
37+
* If a request is already in-flight, return the existing promise.
38+
*/
39+
async fetch<T>(
40+
key: string,
41+
fetchFn: () => Promise<T>,
42+
): Promise<T> {
43+
// Check if a request is already in-flight
44+
if (this.inFlightRequests.has(key)) {
45+
return this.inFlightRequests.get(key) as Promise<T>;
46+
}
47+
48+
// Create new promise and track it
49+
const promise = (async () => {
50+
try {
51+
const result = await fetchFn();
52+
return result;
53+
} finally {
54+
// Remove from tracking after completion (success or failure)
55+
this.inFlightRequests.delete(key);
56+
}
57+
})();
58+
59+
this.inFlightRequests.set(key, promise as Promise<Beneficiary[]>);
60+
return promise;
61+
}
62+
}
63+
64+
const deduplicator = new RequestDeduplicator();
65+
2766
function generateId(): string {
2867
return `ben_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2968
}
3069

31-
export function useBeneficiaries() {
70+
/**
71+
* Fetch beneficiaries from the API with request deduplication.
72+
* All concurrent requests for the same key will share the same in-flight promise.
73+
*/
74+
async function fetchBeneficiariesWithDedup(
75+
userId: string = 'current',
76+
): Promise<Beneficiary[]> {
77+
return deduplicator.fetch(`beneficiaries:${userId}`, async () => {
78+
try {
79+
const response = await fetch(`/api/beneficiaries?userId=${encodeURIComponent(userId)}`);
80+
if (!response.ok) {
81+
throw new Error(`HTTP ${response.status}`);
82+
}
83+
const data = await response.json();
84+
return Array.isArray(data) ? data : [];
85+
} catch (error) {
86+
console.error('Failed to fetch beneficiaries:', error);
87+
// Return empty array on error to allow graceful fallback
88+
return [];
89+
}
90+
});
91+
}
92+
93+
export function useBeneficiaries(options?: { fetchFromApi?: boolean; userId?: string }) {
94+
const { fetchFromApi = false, userId = 'current' } = options || {};
3295
const [beneficiaries, setBeneficiaries] = useState<Beneficiary[]>([]);
3396
const [isLoaded, setIsLoaded] = useState(false);
3497
const [selectedIndex, setSelectedIndex] = useState<number>(-1);
@@ -38,8 +101,35 @@ export function useBeneficiaries() {
38101
setIsMounted(true);
39102
}, []);
40103

104+
// Fetch from API with deduplication if enabled
105+
useEffect(() => {
106+
if (!isMounted || !fetchFromApi || typeof window === 'undefined') return;
107+
108+
let cancelled = false;
109+
110+
(async () => {
111+
try {
112+
const data = await fetchBeneficiariesWithDedup(userId);
113+
if (!cancelled) {
114+
setBeneficiaries(Array.isArray(data) ? data : []);
115+
setIsLoaded(true);
116+
}
117+
} catch (error) {
118+
console.error('Error loading beneficiaries:', error);
119+
if (!cancelled) {
120+
setIsLoaded(true);
121+
}
122+
}
123+
})();
124+
125+
return () => {
126+
cancelled = true;
127+
};
128+
}, [isMounted, fetchFromApi, userId]);
129+
130+
// Load from localStorage if not fetching from API
41131
useEffect(() => {
42-
if (!isMounted || typeof window === 'undefined') return;
132+
if (!isMounted || fetchFromApi || typeof window === 'undefined') return;
43133
try {
44134
const stored = localStorage.getItem(STORAGE_KEY);
45135
if (stored) {
@@ -50,7 +140,7 @@ export function useBeneficiaries() {
50140
setBeneficiaries([]);
51141
}
52142
setIsLoaded(true);
53-
}, [isMounted]);
143+
}, [isMounted, fetchFromApi]);
54144

55145
useEffect(() => {
56146
if (!isLoaded || typeof window === 'undefined') return;

0 commit comments

Comments
 (0)