Files
next-tjwater-frontend/src/features/workbench/peripheral-sample/use-event-location-address.ts
T

67 lines
2.4 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from "react";
import type { EventGpsCoordinate } from "./event-location-coordinate";
export type EventLocationAddressResolver = (coordinate: EventGpsCoordinate, signal: AbortSignal) => Promise<string>;
export type EventLocationAddressLookup = {
pending: boolean;
error: string | null;
changeLocation: (value: string) => void;
retry: () => void;
};
export function useEventLocationAddress({ active = true, coordinate, resolveAddress, onChange }: {
active?: boolean;
coordinate: EventGpsCoordinate | null;
resolveAddress: EventLocationAddressResolver;
onChange: (value: string) => void;
}): EventLocationAddressLookup {
const [pending, setPending] = useState(false);
const [error, setError] = useState<string | null>(null);
const [retryGeneration, setRetryGeneration] = useState(0);
const requestRef = useRef<AbortController | null>(null);
const longitude = coordinate?.[0];
const latitude = coordinate?.[1];
useEffect(() => {
setError(null);
if (!active || longitude === undefined || latitude === undefined) {
setPending(false);
return;
}
const controller = new AbortController();
requestRef.current = controller;
setPending(true);
onChange("");
// Coalesce keyboard adjustments. GPS stays immediate while only the final
// point is reverse-geocoded; a former point's address must never be reused.
const timer = window.setTimeout(async () => {
if (controller.signal.aborted) return;
try {
const address = await resolveAddress([longitude, latitude], controller.signal);
if (!controller.signal.aborted) onChange(address);
} catch (failure) {
if (!controller.signal.aborted) {
setError(failure instanceof Error ? failure.message : "地址查询失败,请重试或手动填写");
}
} finally {
if (!controller.signal.aborted) setPending(false);
}
}, 250);
return () => {
window.clearTimeout(timer);
controller.abort();
if (requestRef.current === controller) requestRef.current = null;
};
}, [active, longitude, latitude, resolveAddress, onChange, retryGeneration]);
const changeLocation = useCallback((value: string) => {
requestRef.current?.abort();
setPending(false);
setError(null);
onChange(value);
}, [onChange]);
return { pending, error, changeLocation, retry: () => setRetryGeneration((current) => current + 1) };
}