Descripción general
En este instructivo, te mostramos cómo agregar un mapa y un marcador a una aplicación de React con @googlemaps/react-wrapper y, luego, cómo integrar el mapa y los marcadores en el estado de la aplicación.
Instala @googlemaps/react-wrapper
Instala y utiliza la biblioteca @googlemaps/react-wrapper para cargar de forma dinámica la API de Maps JavaScript cuando se renderice el componente.
npm install @googlemaps/react-wrapper
Esta biblioteca se puede importar y usar con el siguiente código:
import { Wrapper, Status } from "@googlemaps/react-wrapper";
El uso básico de este componente consiste en unir los componentes secundarios que dependen de la API de Maps JavaScript. El componente Wrapper
también acepta un prop render
para renderizar componentes de carga o controlar los errores de carga de la API de Maps JavaScript.
const render = (status: Status) => {
return <h1>{status}</h1>;
};
<Wrapper apiKey={"YOUR_API_KEY"} render={render}>
<YourComponent/>
</Wrapper>
Cómo agregar un componente de mapa
Es probable que un componente funcional básico use los hooks useRef
, useState
y useEffect
de React.
El componente de mapa inicial tendrá la siguiente firma.
const Map: React.FC<{}> = () => {};
Dado que google.maps.Map
requiere un parámetro Element
como parámetro de constructor, se necesita useRef para mantener un objeto mutable que persistirá durante la vida útil del componente. En el siguiente fragmento, se crea una instancia de un mapa dentro del hook useEffect en el cuerpo del componente Map
.
TypeScript
const ref = React.useRef<HTMLDivElement>(null); const [map, setMap] = React.useState<google.maps.Map>(); React.useEffect(() => { if (ref.current && !map) { setMap(new window.google.maps.Map(ref.current, {})); } }, [ref, map]);
JavaScript
const ref = React.useRef(null); const [map, setMap] = React.useState(); React.useEffect(() => { if (ref.current && !map) { setMap(new window.google.maps.Map(ref.current, {})); } }, [ref, map]);
El hook useEffect
anterior solo se ejecutará cuando cambie el ref
. El componente Map
ahora muestra lo siguiente.
return <div ref={ref} />
Cómo extender el componente del mapa con props adicionales
El componente de mapa básico se puede extender con props adicionales para opciones de mapas, objetos de escucha de eventos y diseños aplicados al elemento div que contiene el mapa. El siguiente código muestra la interfaz expandida de este componente funcional.
interface MapProps extends google.maps.MapOptions {
style: { [key: string]: string };
onClick?: (e: google.maps.MapMouseEvent) => void;
onIdle?: (map: google.maps.Map) => void;
}
const Map: React.FC<MapProps> = ({
onClick,
onIdle,
children,
style,
...options
}) => {}
El objeto style
se puede pasar directamente y configurarse como prop en el objeto div
renderizado.
return <div ref={ref} style={style} />;
onClick
, onIdle
y google.maps.MapOptions
requieren hooks useEffect
para aplicar actualizaciones a google.maps.Map
de forma imperativa.
TypeScript
// because React does not do deep comparisons, a custom hook is used // see discussion in https://github.com/googlemaps/js-samples/issues/946 useDeepCompareEffectForMaps(() => { if (map) { map.setOptions(options); } }, [map, options]);
JavaScript
// because React does not do deep comparisons, a custom hook is used // see discussion in https://github.com/googlemaps/js-samples/issues/946 useDeepCompareEffectForMaps(() => { if (map) { map.setOptions(options); } }, [map, options]);
Los objetos de escucha de eventos requieren un código un poco más complejo para borrar los objetos de escucha existentes cuando se actualiza un controlador como prop.
TypeScript
React.useEffect(() => { if (map) { ["click", "idle"].forEach((eventName) => google.maps.event.clearListeners(map, eventName) ); if (onClick) { map.addListener("click", onClick); } if (onIdle) { map.addListener("idle", () => onIdle(map)); } } }, [map, onClick, onIdle]);
JavaScript
React.useEffect(() => { if (map) { ["click", "idle"].forEach((eventName) => google.maps.event.clearListeners(map, eventName) ); if (onClick) { map.addListener("click", onClick); } if (onIdle) { map.addListener("idle", () => onIdle(map)); } } }, [map, onClick, onIdle]);
Cómo crear un componente de marcador
El componente de marcador usa patrones similares a los del componente de mapa con hooks useEffect
y useState
.
TypeScript
const Marker: React.FC<google.maps.MarkerOptions> = (options) => { const [marker, setMarker] = React.useState<google.maps.Marker>(); React.useEffect(() => { if (!marker) { setMarker(new google.maps.Marker()); } // remove marker from map on unmount return () => { if (marker) { marker.setMap(null); } }; }, [marker]); React.useEffect(() => { if (marker) { marker.setOptions(options); } }, [marker, options]); return null; };
JavaScript
const Marker = (options) => { const [marker, setMarker] = React.useState(); React.useEffect(() => { if (!marker) { setMarker(new google.maps.Marker()); } // remove marker from map on unmount return () => { if (marker) { marker.setMap(null); } }; }, [marker]); React.useEffect(() => { if (marker) { marker.setOptions(options); } }, [marker, options]); return null; };
El componente muestra un valor nulo, ya que google.maps.Map
administra la manipulación del DOM.
Cómo agregar marcadores como componentes secundarios del mapa
Para agregar los marcadores a un mapa, el componente Marker
se pasa al componente Map
con el prop especial children
, como se muestra a continuación.
<Wrapper apiKey={"YOUR_API_KEY"}>
<Map center={center} zoom={zoom}>
<Marker position={position} />
</Map>
</Wrapper>
Se debe realizar un pequeño cambio en el resultado del componente Map
para pasar el objeto google.maps.Map
a todos los elementos secundarios como prop adicional.
TypeScript
return ( <> <div ref={ref} style={style} /> {React.Children.map(children, (child) => { if (React.isValidElement(child)) { // set the map prop on the child component // @ts-ignore return React.cloneElement(child, { map }); } })} </> );
JavaScript
return ( <> <div ref={ref} style={style} /> {React.Children.map(children, (child) => { if (React.isValidElement(child)) { // set the map prop on the child component // @ts-ignore return React.cloneElement(child, { map }); } })} </> );
Cómo vincular el mapa y el estado de aplicación
Con el patrón anterior para las devoluciones de llamada onClick
y onIdle
, la aplicación se puede extender a fin de integrar completamente las acciones de los usuarios, como los clics o los desplazamientos laterales en el mapa.
TypeScript
const [clicks, setClicks] = React.useState<google.maps.LatLng[]>([]); const [zoom, setZoom] = React.useState(3); // initial zoom const [center, setCenter] = React.useState<google.maps.LatLngLiteral>({ lat: 0, lng: 0, }); const onClick = (e: google.maps.MapMouseEvent) => { // avoid directly mutating state setClicks([...clicks, e.latLng!]); }; const onIdle = (m: google.maps.Map) => { console.log("onIdle"); setZoom(m.getZoom()!); setCenter(m.getCenter()!.toJSON()); };
JavaScript
const [clicks, setClicks] = React.useState([]); const [zoom, setZoom] = React.useState(3); // initial zoom const [center, setCenter] = React.useState({ lat: 0, lng: 0, }); const onClick = (e) => { // avoid directly mutating state setClicks([...clicks, e.latLng]); }; const onIdle = (m) => { console.log("onIdle"); setZoom(m.getZoom()); setCenter(m.getCenter().toJSON()); };
Estos hooks se pueden integrar en los elementos del formulario mediante el siguiente patrón, tal como se demuestra con la entrada de latitud.
<label htmlFor="lat">Latitude</label>
<input
type="number"
id="lat"
name="lat"
value={center.lat}
onChange={(event) =>
setCenter({ ...center, lat: Number(event.target.value) })
}
/>
Por último, la aplicación puede hacer un seguimiento de los clics y renderizar los marcadores en la ubicación de cada clic.
{clicks.map((latLng, i) => (<Marker key={i} position={latLng} />))}
Cómo explorar el código
El código de muestra completo se puede explorar a través de las siguientes zonas de prueba del código en línea o mediante la clonación del repositorio de Git.
Prueba la muestra
Clona la muestra
Se requiere Git y Node.js para ejecutar esta muestra de manera local. Sigue estas instrucciones para instalar Node.js y NPM. Con los siguientes comandos, se clonan y se instalan las dependencias y se inicia la aplicación de la muestra.
git clone -b sample-react-map https://github.com/googlemaps/js-samples.git
cd js-samples
npm i
npm start
Para probar otras muestras, cambia a cualquier rama que comience con sample-SAMPLE_NAME
.
git checkout sample-SAMPLE_NAME
npm i
npm start