visgl / react-google-maps Goto Github PK
View Code? Open in Web Editor NEWReact component and hooks library for Google Maps
Home Page: https://visgl.github.io/react-google-maps/
License: MIT License
React component and hooks library for Google Maps
Home Page: https://visgl.github.io/react-google-maps/
License: MIT License
Hello!
I'm testing the library and wanted to add custom Zoom-in Zoom-out buttons, but the map does not update when the zoom values updates.
I created a simple GoogleMap componet like this:
"use client";
import { APIProvider, Map } from "@vis.gl/react-google-maps";
export default function GoogleMap({ zoom }: { readonly zoom: number }) {
const position = { lat: 53.54, lng: 10 };
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? ""}>
<div className="aspect-video">
<Map zoom={zoom} center={position} />
</div>
</APIProvider>
);
}
and then used it on another component like this:
"use client";
import { useState } from "react";
import GoogleMap from "./GoogleMap";
export default function Home() {
const [zoom, setZoom] = useState(10);
return (
<>
<GoogleMap zoom={zoom} />
<button
onClick={() => setZoom(zoom + 1)}
>
Zoom in
</button>
<button
onClick={() => setZoom(zoom - 1)}
>
Zoom out
</button>
<div>Zoom: {zoom}</div>
</>
);
}
When I change the zoom using the parent compent the child component does not update to reflect the new zoom value.
Here is a test repository: https://github.com/MauricioRobayo/google-maps-test
Hello,
I am trying to turn off the Points of Interest (POI) on the Map
component, but there's an issue with the prop not being applied. Here is my code snippet:
import React, { useState } from 'react';
import { APIProvider, Map, AdvancedMarker, Pin, InfoWindow } from '@vis.gl/react-google-maps';
<APIProvider apiKey={API_KEY}>
<Map
onLoadMap={onLoad}
zoom={zoom}
center={center}
disableDefaultUI
style={{height: '100%', width: '100%'}}
// issue is with the below prop - not being applied
styles={[
{
featureType: 'poi',
elementType: 'labels',
stylers: [{ visibility: 'off' }],
},
]}
>
<AdvancedMarker
position={{ lat: latitude, lng: longitude }}
key={key}
title={title}
>
<Pin />
</AdvancedMarker>
<InfoWindow>{Text}</InfoWindow>
</Map>
</APIProvider>
I've tried variations of the styles prop as well. As in, I've tried:
{
featureType: 'poi',
elementType: 'labels',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'poi.business',
elementType: 'labels',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'poi.business',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'poi.business',
elementType: 'all',
stylers: [{ visibility: 'off' }],
},
None of these result in turning of the poi
visibility on the map as far as I can see. I've double checked the Google Map docs as well to make sure of syntax/format: https://developers.google.com/maps/documentation/javascript/style-reference
For clarification, I would like to "turn off" all of these gray colored markers, and only have the black Marker (with the 4
) showing on the map.
I actually tried using AdvancedMarker to render a custom popup box that displays some information.
But whenever I do that while having multiple Markers on the map, the Popup div overlaps some of the Markers and doesn't overlap over the others.
I investigated a little further and noticed that the AdvancedMarker is adding z-index to the markers in an increasing fashion which I believe is causing my Popup div to overlap some and not the others.
I could definitely be wrong in my investigation; if so, any help would be appreciated on the same.
I'll add a code sandbox here: https://codesandbox.io/p/sandbox/busy-ptolemy-wwp52m
I did dig in a little more and found that the markers are being added with a class called -marker-view, which have a transform style on them, because of which I believe the z-index gets to a different stacking context so a global css style doesn't work on it even with !important flag and only an inline css style will do the job, which I don't think is possible as of now.
https://codesandbox.io/p/sandbox/busy-ptolemy-wwp52m
No response
The new directions-example doesn't render since the Routes API isn't enabled for the key we're using for our website.
https://visgl.github.io/react-google-maps/examples/directions
No response
The new release 0.6.1
brought a new issue to the table as described in the title.
Upgraded from 0.6.0
to 0.6.1
Failed to compile.
Module not found: Error: Can't resolve '@vis.gl/react-google-maps' in '/Users/dashboard-ui/src/pages/cars/components'
ERROR in ./src/pages/cars/components/MapTracker.js 5:0-77
Module not found: Error: Can't resolve '@vis.gl/react-google-maps' in '/Users/dashboard-ui/src/pages/cars/components'
Is there any event that I can use to detect zoom changes?
I couldn’t find anything in the docs and type.d.ts file.
When you load a map you get the following warning in the console:
Google Maps JavaScript API has been loaded directly without loading=async.
Use @googlemaps/react-wrapper
instead of custom wrapper
Using Next.js 13.5.6,
@vis.gl/react-google-maps: 0.1.2
<APIProvider apiKey={API_KEY} libraries={['marker']}>
<Map
mapId={'DEMO_MAP_ID'}
zoom={10}
center={{lat: 0, lng: 0}}
gestureHandling={'greedy'}
disableDefaultUI={true}>
<AdvancedMarker
position={{lat: 20, lng: 10}}
draggable={true}>
</AdvancedMarker>
</Map>
</APIProvider>
When using the Autocomplete hook and trying to set extra options I get a console error:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
I used the example code from the hook reference docs.
Using Next.js w/ Typescript
With-in a component I copied the code from the hook docs page but added:
useAutocomplete({
inputField: (inputRef && inputRef.current),
options: {
strictBounds: false,
},
onPlaceChanged
});
removing the 'options' field resolves the useEffect error. I have no useEffect hook within this component..
app-index.js:32 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
at DestinationCard (webpack-internal:///(app-pages-browser)/./app/ui/destination-card.tsx:18:11)
at APIProvider (webpack-internal:///(app-pages-browser)/./node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:253:7)
at div
at CoreMap (webpack-internal:///(app-pages-browser)/./app/ui/map.tsx:37:74)
at InnerLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js:240:11)
at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:72:9)
at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:80:11)
at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:54:9)
at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:62:11)
at LoadingBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js:345:11)
at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:130:11)
at InnerScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js:151:9)
at ScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js:226:11)
at RenderFromTemplateContext (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/render-from-template-context.js:15:44)
at OuterLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js:355:11)
at body
at html
at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:72:9)
at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:80:11)
at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:54:9)
at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:62:11)
at DevRootNotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/dev-root-not-found-boundary.js:32:11)
at ReactDevOverlay (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:66:9)
at HotReload (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:295:11)
at Router (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:169:11)
at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:100:9)
at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:130:11)
at AppRouter (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:451:13)
at ServerRoot (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:128:11)
at RSCComponent
at Root (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:144:11)
In recent discussions we came to the conclusion that it would probably make sense to remove some of the hooks from the library for the first major release in order to limit the API surface and consolidate what we have before possibly reintroducing them if needed.
The hooks that are definitely going to stay:
useMap()
useApiIsLoaded()
/ useApiLoadingStatus()
useMapsLibrary()
(with the pending change to return the library as returned by google.maps.importLibrary
)Trivial hooks that aren't really needed and will be removed (all of their use-cases can be replaced with a combination of useMapsLibrary()
with useState()
/ useEffect()
):
useAutocompleteService()
useDistanceMatrixService()
useElevationService()
useGeocodingService()
useMaxZoomService()
usePlacesService()
(the only thing that differs from the other hooks here is that the places-service needs an element to render attribution information to, but that is something that is going to change in future versions of the google maps API).Finally there are the more complex hooks. Considering those, I started to think that the approach to provide all the functionality of the Maps JavaScript API as is via react-hooks is not the best choice here and we should focus on providing useful abstractions solving specific common use-cases instead.
useAutocomplete()
:
providing a good autocomplete solution is a lot more complicated than can properly be implemented in a simple hook. As such, there would probably only be a very limited number of users this implementation would actually be useful for. The current implementation of google.maps.places.Autocomplete
is also problematic in a react-context due to it creating DOM Elements outside of the scope of the map or the autocomplete input-element. Given the new focus on Web Components in the Maps JavaScript API, it is also likely that google will provide a WC based implementation of the autocomplete input in the future that could possibly solve most of these issues and be used instead.
useDirectionsService()
: this seems to fit well with the Idea to solve a common and specific use-case (rendering directions-results on the map). This should probably be refactored a bit to support a wider range of use-cases.
useStreetViewPanorama()
: the StreetView functionality is a unique feature of the Google Maps API and definitely shouldn't be missing from this library. We should think about refactoring this to fit the common use-cases and possibly provide an alternative component-version to just render StreetView imagery.
We could use a bunch more examples in this library. But which ones to add?
Please comment below and vote on existing comments (using 👍 or similar) for what examples should be added and we'll track that here.
How to make clusterization map + infoWindows for markers ?
When developing with typescript. It can help with autocomplete and viewing the needed props.
This feature will help make development in typescript easier.
Hello, great package, thanks for the huge effort invested into this!
I was looking through the examples tyring to find something on how to use the Geocoding library but could not find anything.
Is there any recommended patter for this?
This is what I came up after exploring a bit, but not sure this is how it is intended to be used:
import {
APIProvider,
Map,
useApiIsLoaded,
useMapsLibrary,
} from "@vis.gl/react-google-maps";
import { useEffect, useState } from "react";
export default function GoogleMap() {
const [address, setAddress] = useState("");
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? ""}>
<Geocoder address={address} />
<label
htmlFor="address"
className="mb-2 text-sm font-medium text-gray-900"
>
Address:
</label>
<input
id="address"
type="text"
className="max-w-sm border border-gray-300 text-gray-900 rounded-lg p-2.5"
onChange={(e) => setAddress(e.target.value)}
/>
</APIProvider>
);
}
function Geocoder({ address }: { readonly address: string }) {
const [position, setPosition] = useState<google.maps.LatLng>();
const geocoding = useMapsLibrary("geocoding");
const isLoaded = useApiIsLoaded();
useEffect(() => {
async function getAddressPosition() {
if (isLoaded && geocoding) {
const geocoder = new geocoding.Geocoder();
const response = await geocoder.geocode({ address });
setPosition(response.results[0].geometry.location);
}
}
getAddressPosition();
}, [address, geocoding, isLoaded]);
return (
<div className="aspect-video max-w-lg">
<Map
key={address} // force a re-render while this lands: https://github.com/visgl/react-google-maps/pull/64
zoom={9}
center={position}
disableDefaultUI
/>
</div>
);
}
If the project is using an invalid API key (for example, an unpaid expired one) the map is rendered for the first time with an error.
If a re-render occurs, the entire page will go blank and an error will be thrown inside useMapEvents
:
TypeError: Cannot read properties of undefined (reading 'remove')
Here is where the error is thrown (I couldn't find the exact file inside the project itself):
function useMapEvents(map, cameraStateRef, props) {
// note: calling a useEffect hook from within a loop is prohibited by the
// rules of hooks, but it's ok here since it's unconditional and the number
// and order of iterations is always strictly the same.
// (see https://legacy.reactjs.org/docs/hooks-rules.html)
for (const propName of eventPropNames) {
// fixme: this cast is essentially a 'trust me, bro' for typescript, but
// a proper solution seems way too complicated right now
const handler = props[propName];
const eventType = propNameToEventType[propName];
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
if (!map) return;
if (!handler) return;
const listener = map.addListener(eventType, ev => {
const mapEvent = createMapEvent(eventType, map, ev);
trackDispatchedEvent(mapEvent, cameraStateRef);
handler(mapEvent);
});
return () => listener.remove();
}, [map, cameraStateRef, eventType, handler]);
}
it happens on return () => listener.remove();
inside the useEffect
a quick fix could be listener && listener.remove()
for example, but I'm not sure of it's implications.
`TypeError: Cannot read properties of undefined (reading 'remove')`
Which happens here
function useMapEvents(map, cameraStateRef, props) {
// note: calling a useEffect hook from within a loop is prohibited by the
// rules of hooks, but it's ok here since it's unconditional and the number
// and order of iterations is always strictly the same.
// (see https://legacy.reactjs.org/docs/hooks-rules.html)
for (const propName of eventPropNames) {
// fixme: this cast is essentially a 'trust me, bro' for typescript, but
// a proper solution seems way too complicated right now
const handler = props[propName];
const eventType = propNameToEventType[propName];
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
if (!map) return;
if (!handler) return;
const listener = map.addListener(eventType, ev => {
const mapEvent = createMapEvent(eventType, map, ev);
trackDispatchedEvent(mapEvent, cameraStateRef);
handler(mapEvent);
});
return () => listener.remove();
}, [map, cameraStateRef, eventType, handler]);
}
specifically here:
return () => listener.remove();
inside the useEffect
unable to zoom in, zoom out, or drag the map.
Whole Code
import {
APIProvider,
InfoWindow,
Map,
Marker,
useMarkerRef,
} from "@vis.gl/react-google-maps";
import { useState } from "react";
interface Props {
lat: number;
lng: number;
fullAddress: string;
}
export const GoogleMap = ({ lat, lng, fullAddress }: Props) => {
const position = { lat, lng };
const [markerRef, marker] = useMarkerRef();
const [isInfoWindowOpen, setIsInfoWindowOpen] = useState(true);
const toggleInfoWindowStatus = (state?: boolean) =>
setIsInfoWindowOpen((previousState) => !previousState);
return (
<APIProvider
apiKey={process?.env?.NEXT_PUBLIC_GOOGLE_MAP_API_KEY as string}
>
<Map
center={position}
zoom={14}
className="h-60 w-full rounded-md border border-C_E1E1E1"
>
<Marker
position={position}
ref={markerRef}
onClick={() => toggleInfoWindowStatus(true)}
/>
{isInfoWindowOpen && (
<InfoWindow
anchor={marker}
onCloseClick={() => toggleInfoWindowStatus(false)}
>
<span className="line-clamp-2 break-words">{fullAddress}</span>
</InfoWindow>
)}
</Map>
</APIProvider>
);
};
NA
Suddenly, Google maps were giving this error on my production application
Therefore, I thought of checking for latest updates of the map's library and found out that my current version was 0.5.0 and the latest one released was 0.6.0.
After upgrading the error in the console shown below is thrown and the app won't start.
This happened after upgrading @vis.gl/react-google-maps
from 0.5.0
to 0.6.0
ERROR in ./node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs 3:0-48
Module not found: Error: Can't resolve 'fast-deep-equal/react' in '/Users/ari/Work/JS/REACT/dashboard-ui/node_modules/@vis.gl/react-google-maps/dist'
Did you mean 'react.js'?
BREAKING CHANGE: The request 'fast-deep-equal/react' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
Hello!
I'm testing the package and installed the latest version at the moment (v0.2.0).
After installing the package and creating a component based on this example, I get the following typescript error:
Could not find a declaration file for module '@vis.gl/react-google-maps'.
Downgrading to v0.1.2 seems to fix the issue.
Here is a test repo: https://github.com/MauricioRobayo/google-maps-test/blob/main/src/app/GoogleMap.tsx
Steps to reproduce:
npm install
npm run build
The build fails because of the error:
any ideas about why useMap is returning null instead of map context?
my map is working ok but i need no use useMap from react
...
...
No response
I'm working on a project and was trying to play around with the positions. Some of the positions work as expected, but when using the positions listed below the component disappears from my map. I've also tried using google.maps.ControlPosition
and entering their respective constants, but that also didn't seem to work. TIA!
TOP_RIGHT;
RIGHT_TOP
RIGHT_CENTER
RIGHT_BOTTOM
BOTTOM_CENTER
BOTTOM_LEFT
BOTTOM_RIGHT
"use client";
import React from "react";
import {APIProvider, Map, Marker, ControlPosition, MapControl,} from "@vis.gl/react-google-maps";
import LocationList from "../LocationList/LocationList";
const GoogleMap = () => {
const position = { lat: 40.69, lng: -74 };
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_MAPS_API_KEY}>
<Map
center={position}
zoom={12}
disableDefaultUI={true}
zoomControl={true}
options={{
zoomControlOptions: { position: ControlPosition.TOP_LEFT }, // <---- Values also don't work here
}}
className="w-1/2 h-1/2"
>
<MapControl position={ControlPosition.BOTTOM_LEFT}> // <------ Here
<LocationList />
</MapControl>
</Map>
</APIProvider>
);
};
export default GoogleMap;
Create a map component and use ControlPosition
or google.maps.ControlPosition
to try and set a position
"@vis.gl/react-google-maps": "^0.4.0"
N/A
Currently, the only way pass a glyph thats an HTMLElement
to a <Pin>
element (as far as i can tell) is to create the element inline in a react component and pass that to the pin like in the following example:
export default function MyComponent() {
const glyphIcon = document.createElement('img');
glyphIcon.src = "/custom-icon.svg";
return (
<>
<AdvancedMarker>
<Pin glyph={glyphIcon}/>
</AdvancedMarker>
</>
)
}
This also causes unexpected issues when trying to render multiple markers using Array.prototype.map()
, where defining the element just once in the component will only render it out on the last AdvancedMarker
, since the native DOM and reacts virtual DOM have some conflicts when using document.createElement()
. The workaround is to move the document.createElement()
part down into map, but still not a clean way to interact with react.
Instead, I think it would be much nicer if we could pass a glyph via react children
, similar to how <MapControl>
currently functions, which renders the children to appropriate HTMLElements
to be used with the google maps api. As this is a react wrapper, removing the need to manually create DOM elements directly would be inline with what this library is trying to achieve.
This would increase code maintainability, decrease weird "non-react-like" code and decrease bug encounters when trying to interface with native DOM Elements and reacts virtual DOM when using this library (as well as just making the API easier to use).
Ideally, when trying to pass a glyph thats an HTMLElement to a <Pin>
, we could do something like this:
<AdvancedMarker>
<Pin background={...}>
<img src="/icon.svg" />
</Pin>
</AdvancedMarker>
Where <Pin>
interprets children
that get passed as glyphs of type Element
(according to types here) and gets rendered out appropriately.
When using the InfoWindow component it renders twice, once empty and then when the children components are rendered it refreshes.
Wasn't sure if it's relevant enough to create a pull request, but this fixed the problem:
/**
* Props for the Info Window Component
*/
export type InfoWindowProps = google.maps.InfoWindowOptions & {
onCloseClick?: () => void;
anchor?: google.maps.Marker | google.maps.marker.AdvancedMarkerElement | null;
};
/**
* Component to render a Google Maps Info Window
*/
export const InfoWindow = (props: PropsWithChildren<IMyInfoWindowProps>) => {
const { children, anchor, onCloseClick, ...infoWindowOptions } = props;
const map = useContext(GoogleMapsContext)?.map;
const infoWindowRef = useRef<google.maps.InfoWindow | null>();
const [contentContainer, setContentContainer] =
useState<HTMLDivElement | null>(null);
// create infowindow once map is available
useEffect(() => {
if (!map) return;
if (!infoWindowRef.current) {
infoWindowRef.current = new google.maps.InfoWindow(infoWindowOptions);
}
// Add content to info window
const el = document.createElement('div');
infoWindowRef.current.setContent(el);
if (onCloseClick) {
infoWindowRef.current.addListener('closeclick', () => {
onCloseClick();
});
}
setContentContainer(el);
// Cleanup info window and event listeners on unmount
return () => {
if (infoWindowRef.current) {
google.maps.event.clearInstanceListeners(infoWindowRef.current);
infoWindowRef.current.close();
}
infoWindowRef.current = null;
el.remove();
setContentContainer(null);
};
}, [map, children, anchor]);
// Open info window after content container is set
useEffect(() => {
if (contentContainer && infoWindowRef.current && anchor) {
infoWindowRef.current.open({ map, anchor });
}
}, [contentContainer, infoWindowRef, anchor, map]);
return (
<>{contentContainer !== null && createPortal(children, contentContainer)}</>
);
};
Just rendering the InfoWindow caused this issue, I will note that I used conditional rendering so the mounting functionality might be related to the issue:
<Map {...mapProps}>
{anchor && <InfoWindow anchor={anchor} />
</Map>
No response
Thanks for this library - is there a plan to integrate the remaining components from the @googlemaps/extended-component-library such as the gmpx-split-layout or the gmpx-place-overview components?
It would be great to find a way to keep both libraries in sync :)
On initial component render, even a few dozen Advanced Markers as map children will take ~ 10 seconds to initially render map. A few hundred can crash the browser.
Interestingly, a workaround is to render 1 marker, then set timeout for > 100ms and render many. OR use legacy Marker component.
Render 100 AdvancedMarkers as map children (just with key, position props)
No response
An Idea for how to simplify the typical use-cases of the useMap
and useMapsLibrary
hooks just crossed my mind and I would love to hear what others think about this.
The most common use-case for the useMap
hook follows this pattern:
function Component() {
const map = useMap();
useEffect(() => {
if (!map) return;
// do stuff with the map instance
return () => {
// cleanup might be needed
};
}, [map]);
return ...;
}
This can be simplified if we move the useEffect into the useMap hook, allowing users to shorten the code to
function Component() {
useMap((map) => {
// do something with the map instance
return () => {
// cleanup
}
});
}
The biggest issue with this would be that we can't specify the dependencies for the effect in a clean way, so I guess an alternative would be to put this into a new hook useMapEffect()
which would follow the structure of useEffect()
and make the semantics of the dependencies array and the returned cleanup-function a bit clearer:
function Component() {
useMapEffect((map) => {
// do stuff with map
return () => {
// cleanup
};
}, [some, additional, dependencies]);
}
Something similar can be done for the typical use-case of useMapsLibrary
:
function Component() {
const [service, setService] = useState(null);
const placesLib = useMapsLibrary('places');
useEffect(() => {
if (!placesLib) return;
setService(new placesLib.PlacesService());
}, placesLib);
}
Since the cleanup-function will typically not be needed with useMapsLibrary
, we could go even one step further and pass through the value returned by the callback, so the example above would become:
function Component() {
const placesService = useMapsLibrary('places',
placesLib => new placesLib.PlacesService()
);
}
what do you think about these? Is that too far from what react usually does or could those be useful?
When I tried to get the coordinates with a onClick event in the map it doesn't show nothing. How to obtain the coordinates?
For example, this doesn't work:
<Map
onClick={(event) => { console.log(event) }}
style={{ width: '400px', height: '400px' }}
zoom={5}
center={{
lat: -17.146,
lng: -65.841
}}
>
<Marker position={{ lat: values.ubicacion.lat, lng: values.ubicacion.lng }} />
</Map>
Due to how this library binds event handlers to the google maps object there is a race condition. Events might be fired before react event handler is attached to the map. The simplest example is onProjectionChanged
:
const App = () => {
const onProjectionChanged = useCallback(function onProjectionChanged() {
console.log("onProjectionChanged");
}, []);
const onBoundsChanged = () => {
console.log("onBoundsChanged");
};
console.log("render app");
return (
<APIProvider apiKey={API_KEY}>
<Map
zoom={3}
center={{ lat: 22.54992, lng: 0 }}
gestureHandling={"greedy"}
disableDefaultUI={true}
onProjectionChanged={onProjectionChanged}
onBoundsChanged={onBoundsChanged}
onClick={() => console.log("click")}
/>
<ControlPanel />
</APIProvider>
);
};
Try to reload map few times, and onProjectionChanged
called not every time.
This happened due to map instance created in one useEffect
and events bound in another. They are executed in diffrent async cycles, so map sometimes managed to fire an event before handler attached.
PS I tried to create a codesandbox with an issue, but in the sandbox no events were fired at all https://codesandbox.io/p/devbox/amazing-bohr-v65hqw
No response
i am using useGoogleMapsScript for the auto complete places api on the same page. this cause issues with with the map.
can we check if the script has already been loaded to avoid this error?
export function GoogleMapsScript({ googleMapsApiKey, children }: LoadScriptProps) {
const { isLoaded, loadError } = useGoogleMapsScript({
googleMapsApiKey: googleMapsApiKey!,
libraries,
})
<APIProvider apiKey={env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}>
</APIProvider>
conflicts...
No response
Have a react component for google.maps.Polyline
similar to <Marker>
/the other components.
Is this something this project plans on doing? I'm asking because I was planning on making a PR that implements this, but wanted to ask if this is in the scope of this project before putting in work into the PR.
<Polyline {...props />
I want to show a custom marker only when the zoom is less than a particular value.
(I'm unsure if I'm following the right practice here).
When I try to zoom in or out the map zooms very very slowly.
Here is my code and here the stackblitz
export const App = () => {
const [zoom, setZoom] = useState(3);
const handleZoomChange = (event: any) => {
setZoom(event?.detail?.zoom);
};
return (
<div style={{ height: '100vh', width: '100vw' }}>
<APIProvider apiKey={API_KEY}>
<Map
zoom={3}
center={{ lat: 22.54992, lng: 0 }}
gestureHandling={'greedy'}
disableDefaultUI={true}
onZoomChanged={handleZoomChange}
mapId={MAP_ID}
>
{zoom < 4 && (
<AdvancedMarker
position={{
lat: 53.58675649147477,
lng: 10.045572975464376,
}}
>
<div
style={{
backgroundColor: 'red',
padding: '2rem',
}}
>
custom marker
</div>
</AdvancedMarker>
)}
</Map>
</APIProvider>
</div>
);
};
Please Note the normal Marker
component doesn't have this issue.
AdvancedMarkers throws an error if used with children and more than one class is added to the className
prop.
I believe this is happening because underneath classList.add
is used:
Try the following code:
import { APIProvider, AdvancedMarker, Map } from "@vis.gl/react-google-maps";
const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? "";
const mapId = process.env.NEXT_PUBLIC_GOOGLE_MAPS_MAP_ID ?? "";
export default function Page() {
const position = { lat: 53.54992, lng: 10.00678 };
return (
<div className="h-screen w-full">
<APIProvider apiKey={apiKey}>
<Map zoom={14} center={position} mapId={mapId}>
<AdvancedMarker
className="class1 class2" // -> This throws an error
position={position}
>
<h2>I am so customized</h2>
<p>That is pretty awesome!</p>
</AdvancedMarker>
</Map>
</APIProvider>
</div>
);
}
The error thrown is:
InvalidCharacterError: Failed to execute 'add' on 'DOMTokenList': The token provided ('class1 class2') contains HTML space characters, which are not valid in tokens.
react-dom.development.js:20665 Uncaught DOMException: Failed to execute 'add' on 'DOMTokenList': The token provided ('class1 class2') contains HTML space characters, which are not valid in tokens.
at eval (webpack-internal:///(app-pages-browser)/./node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:723:35)
at commitHookEffectListMount (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:20998:23)
at commitHookPassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23051:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23156:11)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23153:9)
at recursivelyTraversePassiveMountEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23134:7)
at commitPassiveMountOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:23267:9)
Description of the issue
jklasd
asfjkladf
adf
adf
asdfsad
fdsaf
sa
fdas
Changing the coordinates by selecting different place doesn't reposition the map. new markers are added but the position of map stays as is.
const [zoomLevel, setZoomLevel] = useState(12);
const map = useMap();
const [coords] = useState<Coordinates>(selectedCoords);
return ( <APIProvider apiKey={env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}>
<div style={{ height: "90vh", width: "100%" }}>
<Map
center={coords}
zoom={zoomLevel}
disableDefaultUI={false}
clickableIcons={true}
mapId={process.env.NEXT_PUBLIC_MAP_ID}
/>
</div>
)
No response
A common mistake that is being made is to put the useMap
hook into the component that renders the APIProvider
, where the value of the provider can't be seen and the useMap hook will always return null
.
This can be easily detected and prevented.
In useMap, we should be able to detect if the APIProviderContext exists even if it hasn't been initialised yet and log an error if the context isn't available.
If the API key is expired/wrong. Then the page crashes once we move away from the map page. It throws the following error
Cannot read properties of undefined (reading 'remove')
https://codesandbox.io/p/sandbox/gracious-rain-slhtvx
Now after this If you move again to home page or any other route, It'll crash the page.
No response
In order to be closer to the implementation in the google maps API, we want to change the returned type for the useMapsLibrary
hook from boolean to the actual API Object returned by google.maps.importLibrary
. At the same time we want to drop support for importing multiple libraries at once.
This removes the requirement to access the maps API via the global google.maps.
variables (which seems to be something google wants to move away from in the long term).
So instead of
const libraryLoaded = useMapsLibrary('places');
useEffect(() =>{
if (!libraryLoaded) return;
const svc = new google.maps.places.PlacesService();
// ...
}, [libraryLoaded]);
it would be
const placesLibrary = useMapsLibrary('places');
useEffect(() =>{
if (!placesLibrary) return;
const svc = new placesLibrary.PlacesService();
// ...
}, [placesLibrary]);
I have a map with a list of markers. These markers have a custom icon as a symbol. When I press these markers, the map will move a few pixels to the top. It will also happen if you use normal markers. Please see the video's.
Using the following code
export default function PickupPointMaps({ pickupPoints, onClickMarker }: Props) {
const map = useMap('pickup-points-map');
useEffect(() => {
if (pickupPoints) {
const bounds = new google.maps.LatLngBounds();
pickupPoints.forEach(({ pickupPoint }) => {
bounds.extend({
lat: pickupPoint.geoCoordinates.latitude,
lng: pickupPoint.geoCoordinates.longitude,
});
});
if (map) {
map.fitBounds(bounds);
}
}
}, [pickupPoints, map]);
return (
<Map
id="pickup-points-map"
zoom={12}
center={map?.getBounds()?.getCenter()}
gestureHandling="greedy"
disableDefaultUI={true}
zoomControl={true}
styles={mapStyling}
>
{pickupPoints && pickupPoints.map(({ pickupPoint }) => (
<Marker
key={pickupPoint.id}
position={{
lat: pickupPoint.geoCoordinates.latitude,
lng: pickupPoint.geoCoordinates.longitude,
}}
icon={{
path: 'M0-48c-9.8 0-17.7 7.8-17.7 17.4 0 15.5 17.7 30.6 17.7 30.6s17.7-15.4 17.7-30.6c0-9.6-7.9-17.4-17.7-17.4z',
fillColor: '#ff7600',
fillOpacity: 1,
strokeWeight: 0,
scale: 0.5,
}}
onClick={() => {
onClickMarker(pickupPoint.id);
}}
/>
))}
</Map>
);
}
No response
I would like to open a discussion about Interleaved support.
Imagine that you have a DeckGL project where you want to change between MapLibre and Google Maps with interleave control. For MapLibre you would have something like this
<DeckGL
initialViewState={INITIAL_VIEW_STATE}
layers={layers}
controller={true}
onViewStateChange={...}>
<MapLibre />
</DeckGL>
where the camera control is done by Deck but if you change it to Google Maps with interleave support you would have something like this example where the map camera control is done by the Google Maps component instead DeckGL.
It's difficult to maintain and it can also be difficult to integrate with libraries like NebulaGL that interacts directly with the map events. So my question is if there would be a way to support interleave while keeping Deck as a wrapper of the map, for example:
<DeckGL
initialViewState={INITIAL_VIEW_STATE}
layers={layers}
controller={true}
onViewStateChange={...}>
<Map interleaved={true} {...GOOGLE_MAPS_MAP_OPTIONS} />
</DeckGL>
Hello!
I was testing the library with this simple component:
"use client";
import { APIProvider, ControlPosition, Map } from "@vis.gl/react-google-maps";
export default function GoogleMap() {
const position = { lat: 53.54, lng: 10 };
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? ""}>
<div className="aspect-video max-w-lg">
<Map
zoom={9}
center={position}
disableDefaultUI
zoomControl
zoomControlOptions={{
position: ControlPosition.LEFT_TOP, // <----
}}
/>
</div>
</APIProvider>
);
}
but the zoom control is not being position at the LEFT_TOP:
If I use the same constant from google.maps.ControlPosition
it works as expected. Although they both seem to point to 17
, at runtime google.maps.ControlPosition
seems to evaluate to 5
:
"use client";
import { APIProvider, ControlPosition, Map } from "@vis.gl/react-google-maps";
import { useEffect, useState } from "react";
export default function GoogleMap() {
const position = { lat: 53.54, lng: 10 };
const [zoomControlPosition, setZoomControlPosition] = useState(
ControlPosition.LEFT_TOP
);
useEffect(() => {
setTimeout(() => {
console.log("ControlPosition.LEFT_TOP:", ControlPosition.LEFT_TOP);
console.log(
"google.maps.ControlPosition.LEFT_TOP:",
google.maps.ControlPosition.LEFT_TOP // <---- this logs 5 instead of 17
);
setZoomControlPosition(google.maps.ControlPosition.LEFT_TOP);
}, 2000);
}, []);
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? ""}>
<div className="aspect-video max-w-lg">
<Map
zoom={9}
center={position}
disableDefaultUI
zoomControl
zoomControlOptions={{
position: zoomControlPosition,
}}
/>
</div>
</APIProvider>
);
}
I'm excited to see this library
Is there a plan with clear milestones and ETA for first stable version?
So we can help to focus on these milestones
In ReactJS the <Map zoom={zoom} .. > set via useState changes only once, next consecutive clicks on the AdvancedMarker don't affect the Map zoom (despite the react state changing correctly).
create a react state like [zoom, setZoom] = useState(15)
assign the state to the Map component as <Map zoom={zoom} .. ></Map>
set zoom level in a onClick inside as:
...
[zoom, setZoom] = useState(15)
const markerClick = () => {
setZoom(18);
}
...
<APIProvider apiKey={mapsApi} libraries={['marker']}>
<Map mapId={myMapID} zoom={zoom}>
<AdvancedMarker onClick={() => markerClick()}>
...
</AdvancedMarker>
</Map>
</APIProvider>
then zoom out either with gestures or the zoom controls, and try to click the AdvancedMarker again, it won't zoom at 18 anymore.
No response
Hi everyone, I installed the package in my current project and tried to replicate the basic example. At build-time I get the following error:
./node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs
Can't import the named export 'Children' from non EcmaScript module (only default export is available)
I could be a problem coming from my environment but I wanted to know if anyone has ever encountered something similar.
Thanks in advance.
vis.gl/react-google-maps
const GoogleMapsTest: React.FC = () => {
return (
<APIProvider apiKey={API_KEY}>
<Map
zoom={3}
center={{ lat: 22.54992, lng: 0 }}
gestureHandling={'greedy'}
disableDefaultUI={true}
/>
</APIProvider>
)
}
"@vis.gl/react-google-maps": "^0.4.1"
Dependencies versions (from package.json
):
"@mapbox/mapbox-gl-draw": "1.3.0",
"react": "17.0.2",
"react-scripts": "4.0.3",
No response
Add the ability to add tile layers. For example, a component that could be used to consume the new Air Quality heat map tile endpoint. Currently, I am not sure what the recommended best practice for tile layers would be with this library, perhaps using the DeckGL overlay and adding via DeckGL overlay's layers
prop?
I'm thinking the </Map />
component could accept <TileLayer />
components as children. TileLayer component would accept props similar to its current API: getTileUrl, opacity, getTileSize, etc.
import {APIProvider, Map, TileLayer } from '@vis.gl/react-google-maps';
function App() {
const position = {lat: 53.54992, lng: 10.00678};
return (
<APIProvider apiKey={'YOUR API KEY HERE'}>
<Map center={position} zoom={10}>
<TileLayer getTileUrl={(coord, zoom) => `https://airquality.googleapis.com/v1/mapTypes/US_AQI/heatmapTiles/${zoom}/${coord.x}/${coord.y}key=${API_KEY}`
} />
</Map>
</APIProvider>
);
}
export default App;
The first time that you try to change the Map ID you get this warning
Google Maps JavaScript API: A Map's mapId property cannot be changed after initial Map render
but the change happens. The second time that you try it, it doesn't work.
I think that the problem comes from this line that prevents a new instance of the map from being created
I'm trying to set up the simple example in a NextJS environment, but the map isn't draggable.
Cloning the simple example with vite works, but I cannot get it to work with Next.
Code sandbox: https://codesandbox.io/p/devbox/f5mzxf
This is minimal next setup, showing the map. The map is not draggable even though the same configuration in vite leads to a draggable map.
No response
The current infoWindow class doesn't allow for granular control over styling or even the close button. Having an infoWindow that can render a React.ReactNode (or even a static HTML element but the dev would have full control of it as opposed to what you get from infoWindow) would allow for making maps that look and feel much cleaner with the rest of the web app.
I have implemented a Custom Info Window by extending the google.maps.Overlay class but I was only able to make it render static HTML elements, not React components. Maybe the use of React portals to render React components would work? I haven't tried it yet myself.
Or maybe this is the completely wrong approach and there's a better way of doing it.
I used the Marker
component to create some markers on the map. If I remove (or change the properties of) my <Marker />
s, the initial markers stay on the map.
Here's an example:
https://codesandbox.io/s/pensive-frog-fgztdc?file=/src/index.js
If you click the "move marker" button in the example, you can see that the initial marker at {lat: 0, lng: 0}
stays on the map.
If you click again, the previous marker is removed correctly.
Create a state:
const [center, setCenter] = useState<google.maps.LatLngLiteral>(MAP_CENTER);
Create a map and change the center with onCenterChanged
<Map
center={center}
zoom={zoom}
minZoom={6}
mapTypeControl={false}
onZoomChanged={(e) => {
console.log(e.detail.zoom);
setZoom(e.detail.zoom);
}}
onCenterChanged={(e) => {
console.log(e.detail.center);
setCenter(e.detail.center)
}} >
** markers code removed for brevity **
</Map>
NOTE: I have tried with both Marker and AdvancedMarker
No errors
This issue is very similar to this one but in this case the library doesn't detect a mapTypeId change. I mean, if you keep the same mapId
but you change the mapTypeId
the map is not reloaded
Hello! I'm trying to get a minimal example up and running in Next.js and I'm unable to get the Marker
or InfoWindow
components rendering (AdvancedMarker
works fine).
I believe the issue is due to their use of a fragment <></>
. The compiled version of this (in dist/index.modern.mjs
imports React as React$1
, and most of the references are to this, but for some reason on a fragment, it is referencing React
without the $1
.
import React$1, { useState, useMemo, useEffect, useCallback, useContext, useLayoutEffect, forwardRef, useImperativeHandle, Children, useRef } from 'react';
const Marker = forwardRef((props, ref) => {
const marker = useMarker(props);
useImperativeHandle(ref, () => marker, [marker]);
return /*#__PURE__*/React.createElement(Fragment, null);
});
function useMarkerRef() {
const [marker, setMarker] = useState(null);
const refCallback = useCallback(m => {
setMarker(m);
}, []);
return [refCallback, marker];
}
The error is being displayed as:
The example app where I am running into the error can be found at https://github.com/leighhalliday/next-react-google-maps-bug
If I modify the source to refer to <Fragment></Fragment>
(vs <></>
), the generated dist file works correctly. This makes me think it has something to do with microbundle:
{
"build:microbundle": "microbundle -o dist/index.js -f modern,umd --globals react=React,react-dom=ReactDOM --jsx React.createElement --no-compress --tsconfig tsconfig.build.json",
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.