fullstackreact / google-maps-react

Companion code to the "How to Write a Google Maps React Component" Tutorial
https://www.fullstackreact.com/articles/how-to-write-a-google-maps-react-component/
MIT License
1.64k stars 818 forks source link

Cannot read property 'setMap' of undefined #59

Open alexanderwhatley opened 7 years ago

alexanderwhatley commented 7 years ago

I am getting an error caused by Marker.js, using the google-maps-react downloaded from npm. The code I have is below:

import React from 'react'
import {InfoWindow, Marker, Map, GoogleApiWrapper} from 'google-maps-react'

class RegionalMap extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
    }

    onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };

    onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>
                    <Marker
                        mapCenter="aarr"
                        onClick={this.onMarkerClick}
                        name={'SOMA'}
                        position={{lat: 37.778519, lng: -122.405640}} />
                    <Marker
                        onClick={this.onMarkerClick}
                        name={'Dolores park'}
                        position={{lat: 37.759703, lng: -122.428093}} />

                    <InfoWindow
                        marker={this.state.activeMarker}
                        visible={this.state.showingInfoWindow}
                        onClose={this.onInfoWindowClose}>
                            <div>
                                <h1>{this.state.selectedPlace.name}</h1>
                            </div>
                    </InfoWindow>
                </Map>
        </div>
        )
    }
}

export default GoogleApiWrapper({
    apiKey: MY_API_KEY
})(RegionalMap);

I get an error in the componentDidUpdate function, caused by the line this.marker.setMap(null);. It appears that this error message is occurring because inside the renderMarker function, the google variable is undefined:

                if (!google) {
                    return null;
                }

The funny thing is that when I remove the Marker code from the render function, it works perfectly:

import React from 'react'
import {InfoWindow, Marker, Map, GoogleApiWrapper} from 'google-maps-react'

class RegionalMap extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
    }

    onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };

    onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>

                    <InfoWindow
                        marker={this.state.activeMarker}
                        visible={this.state.showingInfoWindow}
                        onClose={this.onInfoWindowClose}>
                            <div>
                                <h1>{this.state.selectedPlace.name}</h1>
                            </div>
                    </InfoWindow>
                </Map>
        </div>
        )
    }
}

Any help?

tzerb commented 7 years ago

The 'this' variable in your handler function isn't what you think it is. In your constructor, you need to bind the class 'this' to each handler function like so:

this.onMarkerClick = this.onMarkerClick.bind(this);

alexanderwhatley commented 7 years ago

Thanks, but I'm still getting the same error message. This is the revised code:

import React from 'react'
import {InfoWindow, Marker, Map, GoogleApiWrapper} from 'google-maps-react'

class RegionalMap extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
        this.onMarkerClick = this.onMarkerClick.bind(this)
        this.onInfoWindowClose = this.onInfoWindowClose.bind(this)
        this.onMapClicked = this.onMapClicked.bind(this)
    }

    onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };

    onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>
                    <Marker
                        mapCenter="aarr"
                        onClick={this.onMarkerClick}
                        name={'SOMA'}
                        position={{lat: 37.778519, lng: -122.405640}} />
                    <Marker
                        onClick={this.onMarkerClick}
                        name={'Dolores park'}
                        position={{lat: 37.759703, lng: -122.428093}} />

                    <InfoWindow
                        marker={this.state.activeMarker}
                        visible={this.state.showingInfoWindow}
                        onClose={this.onInfoWindowClose}>
                            <div>
                                <h1>{this.state.selectedPlace.name}</h1>
                            </div>
                    </InfoWindow>
                </Map>
        </div>
        )
    }
}

export default GoogleApiWrapper({
    apiKey: MY_API_KEY
})(RegionalMap);
tzerb commented 7 years ago

I'm not sure how it works with the lambas. Try changing them to normal functions. If you're still having trouble, I'd need to know which call is failing. (Maybe set an alert before and after each one.)

alexanderwhatley commented 7 years ago

It's not the lambdas that are causing the problem. The following code fails with the exact same error message as well:

import React from 'react'
import {InfoWindow, Marker, Map, GoogleApiWrapper} from 'google-maps-react'

class RegionalMap extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
        //this.onMarkerClick = this.onMarkerClick.bind(this)
        this.onInfoWindowClose = this.onInfoWindowClose.bind(this)
        this.onMapClicked = this.onMapClicked.bind(this)
    }

    /*onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };*/

    onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>
                    <Marker
                        mapCenter="aarr"
                        name={'SOMA'}
                        position={{lat: 37.778519, lng: -122.405640}} />
                    <Marker
                        name={'Dolores park'}
                        position={{lat: 37.759703, lng: -122.428093}} />

                    <InfoWindow
                        marker={this.state.activeMarker}
                        visible={this.state.showingInfoWindow}
                        onClose={this.onInfoWindowClose}>
                            <div>
                                <h1>{this.state.selectedPlace.name}</h1>
                            </div>
                    </InfoWindow>
                </Map>
        </div>
        )
    }
}

export default GoogleApiWrapper({
    apiKey: MY_API_KEY
})(RegionalMap);

It has something to do with the Marker code. When I comment it out, the other code works just fine.

tzerb commented 7 years ago

Commenting out onInfoWindowClose should solve your problem.

alexanderwhatley commented 7 years ago

Still doesn't work :(

import React from 'react'
import {InfoWindow, Marker, Map, GoogleApiWrapper} from 'google-maps-react'

class RegionalMap extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
        //this.onMarkerClick = this.onMarkerClick.bind(this)
        //this.onInfoWindowClose = this.onInfoWindowClose.bind(this)
        this.onMapClicked = this.onMapClicked.bind(this)
    }

    /*onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };*/

    /*onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };*/

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>
                    <Marker
                        name={'SOMA'}
                        position={{lat: 37.778519, lng: -122.405640}} />
                    <Marker
                        name={'Dolores park'}
                        position={{lat: 37.759703, lng: -122.428093}} />
                </Map>
        </div>
        )
    }
}

export default GoogleApiWrapper({
    apiKey: 'AIzaSyB7VsUzIO-JsnRwh6T5LLpJmW7ByrNrYE8'
})(RegionalMap);
tzerb commented 7 years ago

Interesting... I ran your original class and I couldn't reproduce the error. I wonder if there is something else in your environment causing the problem. (Are you using the latest version of the package?)

alexanderwhatley commented 7 years ago

Interesting indeed. Is it perhaps OS specific (I am on Windows 10)? I am using version 1.0.19, which appears to be the latest. The other installed packages I have are below:

"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-plugin-transform-react-jsx": "^6.8.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"css-loader": "^0.26.1",
"elemental": "^0.6.1",
"firebase": "^3.6.0",
"gmail-api-parse-message": "^1.0.6",
"google-maps-react": "^1.0.19",
"material-auto-rotating-carousel": "^1.1.0",
"material-ui": "^0.16.2",
"react": "^15.4.0",
"react-addons-css-transition-group": "^15.4.1",
"react-dazzle": "^1.2.4",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.4.0",
"react-motion": "^0.4.7",
"react-router": "^3.0.0",
"react-swipeable-views": "^0.9.2",
"react-swipeable-views-utils": "^0.9.2",
"react-tap-event-plugin": "^2.0.1",
"reactfire": "^1.0.0",
"style-loader": "^0.13.1"
tzerb commented 7 years ago

That's the version of the package I'm using, too. I'm also on windows 10. Maybe start with the source of the package and add your class to the example.

alexanderwhatley commented 7 years ago

Sure. Are there any instructions on how to build and run the example on this repo?

tzerb commented 7 years ago

Npm install Npm run dev

tzerb commented 7 years ago

You'll need https://www.npmjs.com/package/cross-env since you're using windows 10

alexanderwhatley commented 7 years ago

Hmm, I installed everything including cross-env, and now I'm getting this:

(C:\Users\Alexander\Anaconda3) C:\Users\Alexander\Downloads\google-maps-react-master>npm run dev

google-maps-react@1.0.18 dev C:\Users\Alexander\Downloads\google-maps-react-master NODE_ENV=development ./node_modules/hjs-webpack/bin/hjs-dev-server.js

'NODE_ENV' is not recognized as an internal or external command, operable program or batch file.

npm ERR! Windows_NT 10.0.14986 npm ERR! argv "C:\Program Files (x86)\nodejs\node.exe" "C:\Users\Alexander\AppData\Roaming\npm\node_modules\npm\bin\npm-cli.js" "run" "dev" npm ERR! node v7.2.1 npm ERR! npm v4.0.3 npm ERR! code ELIFECYCLE npm ERR! google-maps-react@1.0.18 dev: NODE_ENV=development ./node_modules/hjs-webpack/bin/hjs-dev-server.js npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the google-maps-react@1.0.18 dev script 'NODE_ENV=development ./node_modules/hjs-webpack/bin/hjs-dev-server.js'. npm ERR! Make sure you have the latest version of node.js and npm installed. npm ERR! If you do, this is most likely a problem with the google-maps-react package, npm ERR! not with npm itself. npm ERR! Tell the author that this fails on your system: npm ERR! NODE_ENV=development ./node_modules/hjs-webpack/bin/hjs-dev-server.js npm ERR! You can get information on how to open an issue for this project with: npm ERR! npm bugs google-maps-react npm ERR! Or if that isn't available, you can get their info via: npm ERR! npm owner ls google-maps-react npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request: npm ERR! C:\Users\Alexander\Downloads\google-maps-react-master\npm-debug.log

tzerb commented 7 years ago

Read the usage for cross-env

alexanderwhatley commented 7 years ago

Whew, ok I built it from the package, and am getting the exact same error message as before. Here is the Container.js class:

import React from 'react'
var GoogleApiWrapper = require('../dist/index').GoogleApiWrapper
var Map = require('../dist/index').Map
import InfoWindow from '../dist/components/InfoWindow'
import Marker from '../dist/components/Marker'

class Container extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            showingInfoWindow: false,
            activeMarker: {},
            selectedPlace: {},
        };
    }

    onMarkerClick = (props, marker, e) => {
        this.setState({
            selectedPlace: props,
            activeMarker: marker,
            showingInfoWindow: true
        });
    };

    onInfoWindowClose = () => {
        this.setState({
            showingInfoWindow: false,
            activeMarker: null
        });
    };

    onMapClicked = (props) => {
        if (this.state.showingInfoWindow) {
            this.setState({
                showingInfoWindow: false,
                activeMarker: null
            });
        }
    };

    render() {
        return (
            <div id="google-map-holder">
                <Map google={this.props.google}
                        style={{width: '100%', height: '100%', position: 'inherit'}}
                        className={'map'}
                        zoom={14}
                        onClick={this.onMapClicked}>
                    <Marker
                        mapCenter="aarr"
                        onClick={this.onMarkerClick}
                        name={'SOMA'}
                        position={{lat: 37.778519, lng: -122.405640}} />
                    <Marker
                        onClick={this.onMarkerClick}
                        name={'Dolores park'}
                        position={{lat: 37.759703, lng: -122.428093}} />

                    <InfoWindow
                        marker={this.state.activeMarker}
                        visible={this.state.showingInfoWindow}
                        onClose={this.onInfoWindowClose}>
                            <div>
                                <h1>{this.state.selectedPlace.name}</h1>
                            </div>
                    </InfoWindow>
                </Map>
        </div>
        )
    }
}

export default GoogleApiWrapper({
    apiKey: MY_API_KEY
})(Container);

Here is the index.js file:

import React from 'react'
import ReactDOM from 'react-dom'
import {Router, Route, IndexRoute, hashHistory, browserHistory} from 'react-router'

import styles from './global.styles.css';

import Container from './Container'

const routes = (
    <Router history={browserHistory}>
        <Route path='/' component={Container}>
        </Route>
    </Router>
)
const mountNode = document.querySelector('#root')
ReactDOM.render(routes, mountNode);

Here is the global.styles.css file:


@import url("../node_modules/highlight.js/styles/github.css");

#readme {
    padding: 10px;

    img {
        width: 100%;
    }
}

Here is the .babelrc file:


{
  "presets": ["es2015", "react", "stage-0"],
  "env": {
    "development": {
      "presets": ["react-hmre"]
    },
    "test": {
      "presets": []
    }
  }
}

To run it, place the .babelrc file into the main directory, and then create a new folder in the main folder called 'examples', and place the other three files inside. You can then run it using 'npm run dev' from the main directory, after configuring using cross-env if you're on Windows.

jsmapr1 commented 7 years ago

The problem is this line is somehow not showing up in the compiled package version. https://github.com/fullstackreact/google-maps-react/commit/6cddf32369080c5ba9330a0c8ee4ab8e20647aa4

Must have been something with how the file was published to npm, but the code I see in the packaged version in my node_modules (and I'm on the latest version) is this:

        if (this.props.map !== prevProps.map || this.props.position !== prevProps.position) {
          this.marker.setMap(null);
          this.renderMarker();
        }

Not sure why that patch isn't pulling, but it's also not working on the blog post. Check the console. I think this demo is the one having problems: https://www.fullstackreact.com/articles/how-to-write-a-google-maps-react-component/#making-an-infowindow-component

jsmapr1 commented 7 years ago

So when you run the project examples, it works, but if you install in a different project it will not work.

alexanderwhatley commented 7 years ago

@jsmapr1, I believe that the lines of code you are referring to are indeed in the package code. Check out lines 110-113 in dist/components/Marker.js

jsmapr1 commented 7 years ago

Right. Those lines are there. The lines that should be there are these:

if (this.props.map !== prevProps.map || this.props.position !== prevProps.position) {
  if (this.marker) {
    this.marker.setMap(null);
  }
  this.renderMarker();
}

That's the problem.

alexanderwhatley commented 7 years ago

Do you have an estimate on when the npm package will be fixed?

wiredots01 commented 7 years ago

to make a temporary workaround i made it to work like this.

import {InfoWindow, Map, GoogleApiWrapper} from 'google-maps-react' import Marker from '/imports/ui/components/googlemap/Marker'

copied the Marker component from the repo and paste it to your folder then call it as a single component..

just a temporary though while waiting for the npm package updates

americool commented 7 years ago

^^^ I tried the above solution. It worked for me. Note: you also need to get (copy) the String.js file from lib and import it locally. Thanks!

I might try to look into fixing the npm package myself.

nats12 commented 7 years ago

Are there any updates on whether this is getting fixed?

YashGulati commented 7 years ago

It is working sometimes (after many refreshes) but after refreshing, this error comes. Uncaught (in promise) TypeError: Cannot read property 'setMap' of undefined

njho commented 7 years ago

@americool I tried doing what you mentioned.

However I am getting an error with regards to "define" being undefined. How do you get over this?

loanlaux commented 7 years ago

Any eta for the official fix? It's pretty annoying and I don't feel confident pushing the aforementionned quick fix to production once my project is ready to be published.

loanlaux commented 7 years ago

@njho Same thing here, can't even use this fix because of that. Were you able to make it work?

americool commented 7 years ago

Honestly it's been a while since I worked with this lib, it felt somewhat abandoned last time I was on here. You might wanna try https://github.com/istarkov/google-map-react. It's what I used for my last project.

abhi2399 commented 5 years ago

Can anyone tell me how can I handle the error when google maps API is not loading?