import { useCallback, useEffect, useRef, useState } from "react"; const YANDEX_GEOCODER_API_KEY = import.meta.env.VITE_YANDEX_GEOCODER_KEY; export function useYandexMap(enabled, initialCoords = null, initialAddress = "") { const mapRef = useRef(null); const ymapRef = useRef(null); const placemarkRef = useRef(null); const initializedRef = useRef(false); // защита от двойного вызова StrictMode const requestIdRef = useRef(0); // защита от гонки при быстрых кликах/драгах const [coords, setCoords] = useState(initialCoords); const [address, setAddress] = useState(initialAddress ?? ""); const addressRef = useRef(initialAddress ?? ""); const resolveAddress = useCallback((lat, lng) => { const requestId = ++requestIdRef.current; fetch( `https://geocode-maps.yandex.ru/1.x/?apikey=${YANDEX_GEOCODER_API_KEY}&geocode=${lng},${lat}&format=json&lang=ru_RU` ) .then(r => r.json()) .then(data => { // Игнорируем ответ, если пришёл более новый запрос — иначе // устаревший ответ может перезаписать адрес актуальных координат. if (requestId !== requestIdRef.current) return; const found = data?.response?.GeoObjectCollection?.featureMember?.[0] ?.GeoObject?.metaDataProperty?.GeocoderMetaData?.text; const resolved = found || `${lat.toFixed(5)}, ${lng.toFixed(5)}`; setAddress(resolved); addressRef.current = resolved; }) .catch(() => { if (requestId !== requestIdRef.current) return; const fallback = `${lat.toFixed(5)}, ${lng.toFixed(5)}`; setAddress(fallback); addressRef.current = fallback; }); }, []); useEffect(() => { if (!enabled || !mapRef.current) return; if (initializedRef.current) return; // уже инициализировали initializedRef.current = true; const init = () => { if (ymapRef.current) return; const center = initialCoords ? [initialCoords.latitude, initialCoords.longitude] : [55.751574, 37.573856]; const map = new window.ymaps.Map(mapRef.current, { center, zoom: 14, controls: ["zoomControl", "geolocationControl"], }); ymapRef.current = map; if (initialCoords) { const pm = new window.ymaps.Placemark(center, {}, { draggable: true }); map.geoObjects.add(pm); placemarkRef.current = pm; resolveAddress(initialCoords.latitude, initialCoords.longitude); pm.events.add("dragend", () => { const [lat, lng] = pm.geometry.getCoordinates(); setCoords({ latitude: lat, longitude: lng }); resolveAddress(lat, lng); }); } map.events.add("click", e => { const [lat, lng] = e.get("coords"); setCoords({ latitude: lat, longitude: lng }); resolveAddress(lat, lng); if (placemarkRef.current) { placemarkRef.current.geometry.setCoordinates([lat, lng]); } else { const pm = new window.ymaps.Placemark([lat, lng], {}, { draggable: true }); map.geoObjects.add(pm); placemarkRef.current = pm; pm.events.add("dragend", () => { const [la, ln] = pm.geometry.getCoordinates(); setCoords({ latitude: la, longitude: ln }); resolveAddress(la, ln); }); } }); }; if (window.ymaps?.ready) { window.ymaps.ready(init); } return () => { ymapRef.current?.destroy(); ymapRef.current = null; placemarkRef.current = null; initializedRef.current = false; }; }, [enabled, initialCoords, resolveAddress]); return { mapRef, coords, address, addressRef }; }