2

What I am trying to do:

Be able to click a marker and have it show the InfoWindow and then be able to click the marker again to close it or just close the pop up window manually. Also want to be able to open multiple markers at once.

What works:

I can click to open InfoWindow and click again to close the window. If I close one marker then I can open another one.

What doesn't work:

If one marker is already open, and I try to click another to open it I get:

Target container is not a DOM element

The above error occurred in the <InfoWindow> component:

This happens only when I click another marker if another marker is already open. I will post the code below. I have seperated out the MapComponent into a separate file.

MapComponent:

import React from 'react';
import { compose, withProps } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';

import config from '../config';
import { MarkerInfo } from '../components/view';

export const MapComponent = compose(
  withProps({
    googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${config.GOOGLE_API_KEY}&v=3.exp&libraries=geometry,drawing,places`,
    loadingElement: <div style={{ height: `100%` }} />,
    containerElement: <div style={{ height: `500px` }} />,
    mapElement: <div style={{ height: `100%` }} />
  }),
  withScriptjs,
  withGoogleMap
)(props => (
  <GoogleMap defaultZoom={8} center={props.center}>
    {props.isMarkerShown &&
      props.center && (
        <Marker position={props.center} title={"User's Location"} onClick={props.onHomeMarkerClick}>
          <InfoWindow>
            <div>User's Location</div>
          </InfoWindow>
        </Marker>
      )}

    {props.markers.map((marker, i) => {
      const onClick = () => props.onMarkerClick(marker);
      const onCloseClick = () => props.onCloseClick(marker);

      return (
        <Marker key={i} position={marker.position} title={marker.title} onClick={onClick}>
          {marker.showInfo && (
            <InfoWindow onCloseClick={onCloseClick}>
              <div>
                <MarkerInfo marker={marker} />
              </div>
            </InfoWindow>
          )}
        </Marker>
      );
    })}
  </GoogleMap>
));

React Component Container:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import geolib from 'geolib';
import { geolocated } from 'react-geolocated';
import axios from 'axios';

import actions from '../../actions';
import { MapComponent, Geocode } from '../../utils';

class GhostMap extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isMarkerShown: true,
      currentLocation: null,
      radius: 100,
      markers: []
    };

    this.handleMarkerClick = this.handleMarkerClick.bind(this);
    this.handleCloseClick = this.handleCloseClick.bind(this);
    this.handleHomeMarkerClick = this.handleHomeMarkerClick.bind(this);
    this.createMarkersWithinRadius = this.createMarkersWithinRadius.bind(this);
    this.updateRadius = this.updateRadius.bind(this);
  }

  componentDidMount() {
    if (this.props.posts.all) {
      return;
    }
    console.log(this.props);
    this.props
      .fetchPosts()
      .then(data => {
        return data;
      })
      .catch(err => {
        console.log(err);
      });
  }

  componentWillReceiveProps(props) {
    if (props.coords) {
      this.setState({
        currentLocation: {
          lat: props.coords.latitude,
          lng: props.coords.longitude
        }
      });

      this.createMarkersWithinRadius(
        props.posts.all,
        this.state.radius,
        props.coords.latitude,
        props.coords.longitude
      )
        .then(data => {
          this.setState({
            markers: data
          });
          return data;
        })
        .catch(err => {
          console.log(err);
        });
    } else {
      axios
        .get('http://ip-api.com/json')
        .then(response => {
          this.setState({
            currentLocation: {
              lat: response.data.lat,
              lng: response.data.lon
            }
          });
          return this.createMarkersWithinRadius(
            props.posts.all,
            this.state.radius,
            response.data.lat,
            response.data.lon
          );
        })
        .then(data => {
          this.setState({
            markers: data
          });
        })
        .catch(err => {
          console.log(err);
        });
    }
  }

  createMarkersWithinRadius(posts, radius, lat, lng) {
    return new Promise((resolve, reject) => {
      const markers = [];
      const currentLocation = {
        latitude: lat,
        longitude: lng
      };

      posts.map(post => {
        let postGeolocation = {};

        Geocode(post.address, post.city, post.state, post.zipCode)
          .then(response => {
            postGeolocation.lat = response.lat;
            postGeolocation.lng = response.lng;
            const distanceArr = geolib.orderByDistance(currentLocation, [postGeolocation]);
            const miles = (distanceArr[0].distance / 1609.34).toFixed(2);

            if (miles <= radius) {
              markers.push({
                id: post.id,
                position: postGeolocation,
                title: post.title,
                description: post.text,
                image: post.image,
                showInfo: false
              });
            }
            resolve(markers);
          })
          .catch(err => {
            reject(err);
          });
      });
    });
  }

  handleMarkerClick(targetMarker) {
    this.setState({
      markers: this.state.markers.map(
        marker =>
          marker.id === targetMarker.id ? { ...marker, showInfo: !marker.showInfo } : marker
      )
    });
  }

  handleCloseClick(targetMarker) {
    this.setState({
      markers: this.state.markers.map(marker => {
        if (marker._id === targetMarker._id) {
          return {
            ...marker,
            showInfo: false
          };
        }
        return marker;
      })
    });
  }

  handleHomeMarkerClick() {
    this.setState({ isMarkerShown: false });
  }

  updateRadius(event) {
    const radius = event.target.value;
    const posts = this.props.posts.all;
    const { lat, lng } = this.state.currentLocation;
    if (typeof radius !== 'number') {
      alert('Please put in a number');
      return;
    }
    if (this.props.posts.all && this.state.currentLocation) {
      this.createMarkersWithinRadius(this.props.posts.all, radius, lat, lng);
    }
  }

  render() {
    if (this.state.markers.length === 0) {
      return <div>Loading...</div>;
    }

    return (
      <div className="row">
        <div className="col-sm-4">
          <div className="form-group">
            <label htmlFor="text">Radius</label>
            <input
              onChange={this.updateRadius}
              className="form-control"
              id="radius"
              type="Number"
              placeholder="Radius"
            />
          </div>
        </div>
        <div className="col-sm-12">
          <MapComponent
            onMarkerClick={this.handleMarkerClick}
            isMarkerShown={this.state.isMarkerShown}
            onHomeMarkerClick={this.handleHomeMarkerClick}
            center={this.state.currentLocation}
            markers={this.state.markers}
            onCloseClick={this.handleCloseClick}
          />
        </div>
      </div>
    );
  }
}

const stateToProps = state => {
  return {
    posts: state.post
  };
};

const dispatchToProps = dispatch => {
  return {
    fetchPosts: () => dispatch(actions.fetchPosts())
  };
};

const loadData = store => {
  return store.dispatch(actions.fetchPosts());
};

export default {
  loadData: loadData,
  component: connect(stateToProps, dispatchToProps)(
    geolocated({
      positionOptions: {
        enableHighAccuracy: false
      },
      userDecisionTimeout: 5000
    })(GhostMap)
  )
};
Taylor Austin
  • 5,407
  • 15
  • 58
  • 103
  • 1
    I also ran into this today and would like to know how to resolve it. In my case I see this occur when the infoWindow is open and I try to remove one of the other Markers – Stuart Casarotto Nov 12 '17 at 05:24
  • Yeah it has something to do with the InfoWindow being open and doing another action to another marker while it is still open I think – Taylor Austin Nov 12 '17 at 14:52
  • 1
    If I were you I would keep an eye out here: https://github.com/tomchentw/react-google-maps/issues/691 . Also in the meantime I am have simply wrapped my InfoWindow in a component and catch the error in ```componentDidCatch``` . If I come up with anything I will keep you posted. I Will be triyng to debug this tonight and tomorrow – Stuart Casarotto Nov 12 '17 at 22:44
  • Okay I'll keep that in mind. Just post that you don't on here – Taylor Austin Nov 13 '17 at 01:08
  • It seems like it was fixed in 9.2.3 -> https://github.com/tomchentw/react-google-maps/blob/master/CHANGELOG.md . My setup is now working at least. – Stuart Casarotto Nov 27 '17 at 17:21
  • I'll give it a try soon ! – Taylor Austin Nov 27 '17 at 17:23
  • @StuartCasarotto Im still getting the same error, not sure what I am doing wrong – Taylor Austin Nov 27 '17 at 18:05

0 Answers0