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) { const [pois, setPois] = useState([]); const debounceRef = useRef | null>(null); const abortControllerRef = useRef(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; }