(React) Opening a Modal on Google Maps Marker Click

Question

Update: Added Dandy's I can now access the this.setState function within componentDidMount if I bind this to componentDidMount, but because the this.setState is within componentDidMount, this.setState is not a function.

I think I have to build the map outside of the componentDidMount, but I am not sure where to start at this point

Original:

So I am building a Google Map in React, and I would like to open a modal on marker click.

I have the Modal in my render and

marker.addListener('click', function() {
          infowindow.open(map, marker);
          this.setState({ showModal: true });
        });

within componentDidMount, but I see I can't alter the state in componentDidMount... Any suggestions as to how I could get my modal to show up on marker click?

import React from 'react';
import {Link} from 'react-router';
import {
    Row,
    Col,
    FormGroup,
    ControlLabel,
    FormControl,
    Button,
    Grid,
    Modal,
    Popover,
    Tooltip,
    OverlayTrigger
} from 'react-bootstrap';

export class Maps extends React.Component {
    constructor(props) {
        super(props);
            userPosition: {lat: 40.7128, lng: -74.0059},
            defaultCenter: {
                lat: 40.7128,
                lng: -74.0059
            },
            showModal: false,
        };
        this.close = this.close.bind(this);
        this.open = this.open.bind(this);
        this.componentDidMount = this.componentDidMount.bind(this);
    }

    close() {
        this.setState({ showModal: false });
      }

    open() {
        this.setState({ showModal: true });
      }
  
    componentDidMount() {
        // this.getCurrentPosition();
        var contentString = '<div id="content">'+
            '<div id="siteNotice">'+
            '</div>'+
            '<h1 id="firstHeading" class="firstHeading">New York</h1>'+
            '<div id="bodyContent">'+
            '<p>content string</p>'+
            '</div>'+
            '</div>';

        let sourland = {lat: 40.473927, lng: -74.694482};

        var map = new google.maps.Map(document.getElementById('map'), {
            zoom: 12,
            center: {
                lat: 40.7128,
                lng: -74.0059
            }
        });

        var infowindow = new google.maps.InfoWindow({
          content: contentString,
          maxWidth: 200
        });

        var marker = new google.maps.Marker({
          position: sourland,
          map: map,
          title: 'Uluru (Ayers Rock)'
        });

        // modal code
        marker.addListener('click', function() {
          infowindow.open(map, marker);
          this.setState({ showModal: true });
        });
    }


    render(){
        console.log(this.state);

        const popover = (
          <Popover id="modal-popover" title="popover">
            very popover. such engagement
          </Popover>
        );
        const tooltip = (
          <Tooltip id="modal-tooltip">
            wow.
          </Tooltip>
        );

        return (
            <div>
                <div id="map" className="map-container"></div>

                <Button
                  bsStyle="primary"
                  bsSize="large"
                  onClick={this.open}
                >
                  Launch demo modal
                </Button>

                <Modal show={this.state.showModal} onHide={this.close}>
                  <Modal.Header closeButton>
                    <Modal.Title>Sourland Mountain Preserve Climbs</Modal.Title>
                  </Modal.Header>
                  <Modal.Body>
                    <h4>Sourland Mountain Preserve</h4>
                    <p>Climbing is allowed in blank, but watch out for blank.</p>

                    <h4>This is a Popover</h4>
                    <p>there is a <OverlayTrigger overlay={popover}><a href="#">popover</a></OverlayTrigger> here</p>

                    <h4>Tooltips in a modal</h4>
                    <p>there is a <OverlayTrigger overlay={tooltip}><a href="#">tooltip</a></OverlayTrigger> here</p>
                  </Modal.Body>
                  
                  <Modal.Footer>
                    <Button onClick={this.close}>Close</Button>
                  </Modal.Footer>
                </Modal>
            </div>
        );
    }
}


Show source
| javascript   | reactjs   | ecmascript-6   | google-maps   2016-10-17 03:10 2 Answers

Answers ( 2 )

  1. 2016-10-17 05:10

    From your code, it looks like you're not setting the state correct in your constructor. As a result, the state of 'showModal' isn't accessible/doesn't exist.

    Make sure your constructor is configured to set the state of the component:

    Change:

    constructor(props) {
        super(props);
            userPosition: {lat: 40.7128, lng: -74.0059},
            defaultCenter: {
                lat: 40.7128,
                lng: -74.0059
            },
            showModal: false,
        };
    

    Into this:

    constructor(props) {
        super(props);
        this.state = { 
            userPosition: {lat: 40.7128, lng: -74.0059},
            defaultCenter: {
                lat: 40.7128,
                lng: -74.0059
            },
            showModal: false
        }
    };
    

    This should ensure that using this.setState() correctly updates the state of the component from true to false.

  2. 2016-10-18 19:10

    After implementing Dandy's constructor fix, I rewrote the map component in ES6 using Daniel Andrews's ES6 React GMap Component post as reference.

    Because this component is used in a Meteor 1.4 app, I had to use strict ES6 (no ES5 or ES7+ as in the Andrews example)

    export class Maps extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            userPosition: {lat: 40.7128, lng: -74.0059},
            defaultCenter: {
                lat: 40.7128,
                lng: -74.0059
            },
            zoom: 10,
            showModal: false,
        };
        this.close = this.close.bind(this);
        this.open = this.open.bind(this);
    }
    
    componentDidMount() {
      this.map = this.createMap()
      this.marker = this.createMarker()
      this.infoWindow = this.createInfoWindow()
    
      google.maps.event.addListener(this.map, 'zoom_changed', ()=> this.handleZoomChange())
    
      google.maps.event.addListener(this.marker, 'click', ()=> this.handleMarkerClick())
    }
    
    componentDidUnMount() {
      google.maps.event.clearListeners(map, 'zoom_changed')
    }
    
    createMap() {
      let mapOptions = {
        zoom: this.state.zoom,
        center: this.mapCenter()
      }
      return new google.maps.Map(this.refs.mapCanvas, mapOptions)
    }
    
    mapCenter() {
      return new google.maps.LatLng(
        this.state.defaultCenter.lat,
        this.state.defaultCenter.lng
      )
    }
    
    createMarker() {
      return new google.maps.Marker({
        position: this.mapCenter(),
        map: this.map
      })
    }
    
    createInfoWindow() {
      let contentString = "<div class='InfoWindow'>I'm a Window that contains Info Yay</div>"
      return new google.maps.InfoWindow({
        map: this.map,
        anchor: this.marker,
        content: contentString
      })
    }
    
    handleMarkerClick(){
        console.log("ow");
        this.setState({
          showModal: true
        });
        this.infowindow.open(map, marker);
    }
    
    handleZoomChange() {
      this.setState({
        zoom: this.map.getZoom()
      })
    }
    
    close() {
        this.setState({ showModal: false });
      }
    
    open() {
        this.setState({ showModal: true });
      }
    
      render() {
    
        const popover = (
          <Popover id="modal-popover" title="popover">
            very popover. such engagement
          </Popover>
        );
        const tooltip = (
          <Tooltip id="modal-tooltip">
            wow.
          </Tooltip>
        );
    
        return <div>
        <div className="GMap">
          <div className='UpdatedText'>
            <p>Current Zoom: { this.state.zoom }</p>
          </div>
          <div className='GMap-canvas' ref="mapCanvas">
          </div>
        </div>
    
          <Modal show={this.state.showModal} onHide={this.close}>
            <Modal.Header closeButton>
              <Modal.Title>Sourland Mountain Preserve Climbs</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <h4>Sourland Mountain Preserve</h4>
              <p>Climbing is allowed in blank, but watch out for blank.</p>
    
              <h4>This is a Popover</h4>
              <p>there is a <OverlayTrigger overlay={popover}><a href="#">popover</a></OverlayTrigger> here</p>
    
              <h4>Tooltips in a modal</h4>
              <p>there is a <OverlayTrigger overlay={tooltip}><a href="#">tooltip</a></OverlayTrigger> here</p>
    
              <hr />
    
            </Modal.Body>
            <Modal.Footer>
              <Button onClick={this.close}>Close</Button>
            </Modal.Footer>
          </Modal>
        </div>
      }
    

    }

◀ Go back