我正在尝试做的事情:
能够单击标记并使其显示InfoWindow
然后能够再次单击标记以将其关闭或仅手动关闭弹出窗口。还希望能够一次打开多个标记。
什么有效:
我可以单击打开InfoWindow
并再次单击关闭窗口。如果我关闭一个标记,然后我可以打开另一个。
什么不起作用:
如果一个标记已经打开,并且我尝试单击另一个标记将其打开,我会得到:
Target container is not a DOM element
The above error occurred in the <InfoWindow> component:
如果另一个标记已打开,则仅当我单击另一个标记时才会发生这种情况。我将在下面发布代码。我已将其分离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>
));
反应组件容器:
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)
)
};