import { useEffect } from 'react'; import { useMap, useMapEvents } from 'react-leaflet'; // v4 replaces the v1 `ref`/`.leafletElement` + handleLeafletLoad pattern: this // child runs inside and hands the ready Leaflet map to a class // parent once, so the parent can drive it imperatively (setView, fitBounds, // graphicScale, subsUnion, …). export const MapReady = ({ onReady }) => { const map = useMap(); useEffect(() => { if (!onReady) return undefined; let cancelled = false; let delivered = false; // El efecto de montaje puede correr antes de que el contenedor tenga tamaño. // Si entregamos el mapa entonces, un `getBounds()` del padre lanza y se queda // sin bounds: en /fires eso dejaba la suscripcion por viewport sin crear y el // mapa vacio (0 capas) pese a haber 7470 fuegos. Esperamos a que el mapa este // listo Y tenga tamaño real; entregamos una sola vez. let observer = null; let poll = null; const stopWatching = () => { if (observer) { observer.disconnect(); observer = null; } if (poll) { clearInterval(poll); poll = null; } }; const deliver = () => { if (cancelled || delivered) return; map.invalidateSize(); const size = map.getSize(); if (!size || size.x === 0 || size.y === 0) return; delivered = true; stopWatching(); onReady(map); }; map.whenReady(deliver); map.on('resize', deliver); // Y aquí está lo que faltaba. El contenedor puede coger su tamaño DESPUÉS // del montaje —en producción la hoja de estilos se aplica más tarde que el // primer render de React, cosa que en desarrollo no pasa porque el CSS lo // inyecta el propio JS— y Leaflet no vigila su contenedor: solo emite // `resize` cuando ALGUIEN llama a invalidateSize(). Como nadie lo hacía, el // mapa se quedaba con tamaño 0 para siempre: sin capa base, sin teselas y // sin la suscripción por viewport que crea el padre. Es el "mapa gris" de // /fires en staging, que en local no se veía justamente porque en // desarrollo el CSS llega a tiempo. const container = map.getContainer(); if (container && typeof ResizeObserver !== 'undefined') { observer = new ResizeObserver(deliver); observer.observe(container); } // Red de seguridad para navegadores sin ResizeObserver y para cambios de // tamaño que no lo disparen; se apaga sola al entregar o a los 10 s. let attempts = 0; poll = setInterval(() => { attempts += 1; deliver(); if (delivered || attempts > 20) stopWatching(); }, 500); return () => { cancelled = true; map.off('resize', deliver); stopWatching(); }; }, [map]); // eslint-disable-line react-hooks/exhaustive-deps return null; }; // Bridges Leaflet map events to callbacks (replaces the v1 onMoveend / // onViewportChanged / onZoomend props on ). export const MapEvents = ({ handlers }) => { useMapEvents(handlers || {}); return null; };