perfect-postcode/frontend/src/hooks/usePOIData.ts
2026-05-14 20:42:48 +01:00

64 lines
2 KiB
TypeScript

import { useState, useEffect, useRef } from 'react';
import type { Bounds, POI, POIResponse } from '../types';
import { apiUrl, logNonAbortError, authHeaders } from '../lib/api';
const DEBOUNCE_MS = 150;
export function usePOIData(bounds: Bounds | null, selectedCategories: Set<string>) {
const [pois, setPois] = useState<POI[]>([]);
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const requestIdRef = useRef(0);
useEffect(() => {
requestIdRef.current += 1;
const requestId = requestIdRef.current;
if (!bounds || selectedCategories.size === 0) {
abortControllerRef.current?.abort();
setPois([]);
return;
}
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
debounceRef.current = setTimeout(async () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
abortControllerRef.current = new AbortController();
try {
const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
const categoriesStr = Array.from(selectedCategories).join(',');
const params = new URLSearchParams({
categories: categoriesStr,
bounds: boundsStr,
});
const res = await fetch(
apiUrl('pois', params),
authHeaders({
signal: abortControllerRef.current.signal,
})
);
if (!res.ok) throw new Error(`POIs fetch failed: HTTP ${res.status}`);
const json: POIResponse = await res.json();
if (requestIdRef.current !== requestId) return;
setPois(json.pois || []);
} catch (err) {
logNonAbortError('Failed to fetch POIs', err);
}
}, DEBOUNCE_MS);
return () => {
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
abortControllerRef.current?.abort();
};
}, [bounds, selectedCategories]);
return pois;
}