I previously posted this on StackOverflow but since this is less of a “how to” question and more a case of “there might be a bug with the Overlay component”, I decided to repost it here.
When leaving the page that has the map with some OverlayView’s, I get the error message:
Uncaught TypeError: Cannot read property 'overlayMouseTarget' of null
Here is the code with the GoogleMap component:
const GMap = withGoogleMap((props) => (
<GoogleMap
ref={props.onMapMounted}
defaultZoom={12}
zoom={props.zoom}
onDragEnd={props.onZoomChanged}
onZoomChanged={props.onZoomChanged}
defaultCenter={props.defaultCenter}
center={props.center}
>
{
props.markers && props.markers.map((data, index) => (
<OverlayView
key={index}
position={{
lat: data.get('loc').get('geo').get('lat'),
lng: data.get('loc').get('geo').get('lng'),
}}
mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
getPixelPositionOffset={(width, height) => ({ x: -(width / 2), y: -(height) })}
>
<WnmMarker
text={data.get('text')}
highlight={data.get('highlight')}
/>
</OverlayView>
))
}
</GoogleMap>
));
Could it be that something is missing in the componentWillUnmount?
Maybe the official google maps API does no longer use overlayMouseTarget in their key, so that returns undefined.
Would you mind look into it?
overlayMouseTarget seems to be still in use. If it didn’t I would get that error on mount but I only get it at unMount. Could it have anything to do with the onRemove function?
If it can help, after reading #405 , I added key={Math.random()} to <OverlayView /> and the error message disappeared.
I'm not sure if this is related but the width and height passed down to getPixelPositionOffset is sometimes wrong. I receive (0px, 13px) whereas my markers are (24px, 32px). It leads to a misplaced overlay on the map.
Here is the code with the GoogleMap component
<GoogleMap ref={this.handleMapRef} {...this.optionalProps}
defaultCenter={defaultCenter} defaultOptions={defaultOptions} defaultZoom={defaultZoom}
onDragStart={this.handleDragStart} onIdle={this.handleIdle}>
{this._selectedMarker && (
<OverlayView /***** --> key={Math.random()} <-- *****/ position={{ lat, lng }}
getPixelPositionOffset={this.getPixelPositionOffset}
defaultMapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}>
<img src={getMarkerIcon(poiType, true)} />
</OverlayView>
)}
<MarkerClusterer onMarkerClick={this.handleMarkerClick} />
</GoogleMap>
Any clue?
None on my side for map panes.
For the random width / height given to getPixelPositionOffset, I just force the output when entries are undefined / not matching icon sizes as my icons have fixed dimensions.
I need key={Math.random()} even when using the code from example https://tomchentw.github.io/react-google-maps/basics/overlay-view
https://github.com/tomchentw/react-google-maps/blob/master/src/lib/OverlayView.js#L110
mapPanes is undefined
I load map API async, this is the only difference from the example.
Any updates on this?
I'm having the same issue. "Cannot read property 'overlayLayer' of null"
For me, the problem occurs when I zoom in and out quickly when all the Overlays are shown on the map.
Here's my solution:
The error (at least for me) is pretty benign and it only shows up once when the OverlayView is first attempted to be rendered. With that in mind, I tried delaying its render until _after_ the rest of the Google Map is mounted. So essentially, you're allowing the map some time to load before adding the Overlay. Bonus, no need to add a key at all.
class Map extends Component {
constructor () {
super()
this.state = {
loaded: false
}
}
componentDidMount () {
this.setState({ loaded: true })
}
render () {
return (
<GoogleMap>
{this.state.loaded &&
<OverlayView
position={this.props.center}
mapPaneName={OverlayView.OVERLAY_LAYER}
getPixelPositionOffset={getPixelPositionOffset}
>
<div>My Overlay</div>
</OverlayView> }
</GoogleMap>
)
}
}
I'm still experiencing this problem even if I use the solution proposed by @ericconstantinides
I was able to isolate and reproduce the error here: https://github.com/gomezgoiri/reproducing-react-gmaps-error
As you can see, it only happens from time to time (in Firefox more often than in Chrome).
The reason why I change the location so frequently is to emulate a behavior I sometimes get when I connect a component to Redux. This could be possibly improved to avoid drawing the component so often, but I guess that when it comes to the existence of this error how it is produced does not matter so much... right?
I've been having the same problems, after digging around quite a bit it seems that the bug happens when the component is re-rendered before Google maps has completed initializing the map/panes etc.
The re-rendering causes OverlayView.componentDidUpdate to be called which then triggers OverlayView.draw to be called and this is where the error occurs.
My first guess is that a check for presence of this.containerElement in componentDidUpdate before calling draw maybe all that is needed to fix this problem and when using the code below as a quick test of that theory on @gomezgoiri repo it did appear to fix it.
But that's just a theory based off the few methods in this class I've been inspecting whilst debugging this problem so there maybe unintended side effects of doing that check.
class CustomOverlay extends OverlayView{
// quick test code to see if containerElement presence check meant error did not occur
componentDidUpdate(prevProps, prevState){
if (this.containerElement){
super.componentDidUpdate(prevProps, prevState)
}
}
}
@tomchentw, is it possible this is cause and if so, what's the recommended solution?
It seems like still an issue since it happens on 9.0.1.
Adding key to OverlayView or wait till component to be mounted, didn't work for me.
I could avoid by adding existence check before drawing of OverlayVIew like below.
draw() {
const { mapPaneName } = this.props
invariant(
!!mapPaneName,
`OverlayView requires either props.mapPaneName or props.defaultMapPaneName but got %s`,
mapPaneName
)
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapPanes
const mapPanes = this.state[OVERLAY_VIEW].getPanes()
if(mapPanes){
mapPanes[mapPaneName].appendChild(this.containerElement)
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
React.Children.only(this.props.children),
this.containerElement,
this.onPositionElement
)
}
}
However I'm not sure it's a correct way to fix it.
Any suggestions?
Added:
In some cases I needed to make sure this.containerElement exists as well, so modified to if(mapPanes && this.containerElement)
Taking @foloinfo's advice, I implemented the following class in my project that eliminates the errors. Hopefully this will help somebody else, too!
import React from 'react';
import ReactDOM from 'react-dom';
import invariant from 'invariant';
import { OverlayView } from 'react-google-maps';
import { OVERLAY_VIEW } from 'react-google-maps/lib/constants';
export default class CustomOverlayView extends OverlayView {
// Override draw function to catch errors with map panes being undefined to prevent console errors
draw() {
const { mapPaneName } = this.props;
invariant(
!!mapPaneName,
`OverlayView requires either props.mapPaneName or props.defaultMapPaneName but got %s`,
mapPaneName
);
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapPanes
const mapPanes = this.state[OVERLAY_VIEW].getPanes();
if (mapPanes && this.containerElement) { // <-- Add conditional to ensure panes and container exist before drawing
mapPanes[mapPaneName].appendChild(this.containerElement);
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
React.Children.only(this.props.children),
this.containerElement,
this.onPositionElement
);
}
}
}
I was getting an error when removing overlays (see below) so I also had to override onRemove in @owap 's solution. — Super Hack
onRemove() {
if (this.containerElement) {
return super.onRemove();
}
}
The error was
Uncaught TypeError: Cannot read property 'parentNode' of undefined
at CustomOverlayView.onRemove (OverlayView.js:184)
Hello,
how did you trigger this error @shanimal ?
I get the same error logged from some users in production but can't reproduce it to know where it comes from. I'm running react-google-maps v9.4.5.
Thank you,
Sorry, I haven't had the time to figure it out.
We use redux with several stores that each have an Immutable.List() of markers. When the user changes to a different section of our site we provide the map with data from the store corresponding to the given section of the site. Each Section of markers updates via callback that dispatches updates from a socket connection. Updates seem to be fine, but when we switch to a different stores marker List() is when it seems to choke.
Following code worked in my case:
`
overlay: any;
getRef = (r) => this.overlay = r;
shouldComponentUpdate() {
return !this.overlay || !!this.overlay.getPanes();//Error if you don't have this condition: Cannot read property 'overlayLayer' of undefined
}
render() {
const { center } = this.props;
return <OverlayView
ref={this.getRef}
getPixelPositionOffset={getPixelPositionOffset}
position={center as any} mapPaneName={OverlayView.OVERLAY_LAYER}>
<div style={{ background: `green`, border: `1px solid #ccc`, padding: 15 }}>
<h1>OverlayView</h1>
<button style={{ height: 60 }}>
I have been clicked time
</button>
</div>
</OverlayView>
}
`
I was having this issue but with the draw method, here is the solution for those using Typescript.
import * as React from "react";
import ReactDOM from "react-dom";
import { OverlayView } from "react-google-maps";
import { OVERLAY_VIEW } from "react-google-maps/lib/constants";
import { assert } from "../../util/Assertion";
export default class CustomOverlayView extends OverlayView {
containerElement: HTMLElement | null = null;
onRemove() {
if (this.containerElement) {
// @ts-ignore: Unreachable code error
super.onRemove();
}
}
draw() {
const { mapPaneName } = this.props;
assert.equal(!!mapPaneName, true, () => ({
message: `OverlayView requires either props.mapPaneName or props.defaultMapPaneName but got ${mapPaneName}`,
context: "CustomOverlayView",
}));
// @ts-ignore: Unreachable code error
const mapPanes = this.state[OVERLAY_VIEW].getPanes();
if (mapPanes && this.containerElement) {
// <-- Add conditional to ensure panes and container exist before drawing
// @ts-ignore: Unreachable code error
mapPanes[mapPaneName].appendChild(this.containerElement);
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
React.Children.only(this.props.children),
this.containerElement,
// @ts-ignore: Unreachable code error
this.onPositionElement
);
}
}
}
Make sure you have this rule set to false on the tslint.json file "ban-ts-ignore": false.
Note
I don't agree with using // @ts-ignore a lot, but this made it not complain otherwise I would have have to redeclare the module with the right types. Basically redeclaring this:
declare module 'react-google-maps/lib/components/OverlayView' {
import { Component, ReactNode } from 'react'
export interface OverlayViewProps {
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral
children?: ReactNode
getPixelPositionOffset?: (width: number, height: number) => { x?: number, y?: number }
mapPaneName?: string
position?: google.maps.LatLng | google.maps.LatLngLiteral
}
export default class OverlayView extends Component<OverlayViewProps> {
static FLOAT_PANE: string
static MAP_PANE: string
static MARKER_LAYER: string
static OVERLAY_LAYER: string
static OVERLAY_MOUSE_TARGET: string
getPanes(): google.maps.MapPanes
getProjection(): google.maps.MapCanvasProjection
}
}
I'm getting this with version 9.4.5 @tomchentw It seems that others have been having issues since this issue was closed, and the workaround is to override the OverlayView class :( - maybe reopen this issue?
@joaoreynolds This repo is unmaintained more than a year, please refer to npm @react-google-maps/api
We have docs, examples and community.
@JustFly1984
I have tried your version from your repo, I was struggling even to initialize the basic setup of the map!
@jetonk we have a lot of examples and docs, look at gatsby-example in our repo.
Most helpful comment
Taking @foloinfo's advice, I implemented the following class in my project that eliminates the errors. Hopefully this will help somebody else, too!