Where exactly do I call fitBounds or map.fitbounds. I am confused where to put it once I have the bounds or how to use it. I have set the bounds to a local state of bounds.
I have looked at this post here react-google-maps: how to use fitBounds, panBy, panTo, panToBounds public APIs? and either I just do it different or something because it does not make much since to me.
I have created a bounds const and then each time I make a marker I have added each of those to the bounds doing bounds.extends(Marker in here)
The code is a little messy so I will point out where I do this in it. Inside the filterItemsByRadius is where I create and set the bounds. At the end of the map function I think set the state of bounds to the bounds.
/* global google */
import { default as React, Component } from 'react';
import raf from 'raf';
import canUseDOM from 'can-use-dom';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import {
withGoogleMap,
GoogleMap,
Circle,
InfoWindow,
Marker,
withScriptjs,
} from 'react-google-maps';
import geolib from 'geolib';
import { geolocated } from 'react-geolocated';
import ItemList from './ItemList';
import { Col } from 'react-bootstrap';
import Paper from 'material-ui/Paper';
import Img from 'react-image';
import RaisedButton from 'material-ui/RaisedButton';
import FontIcon from 'material-ui/FontIcon';
import CreateRadius from './CreateRadius';
import offerActions from '../../redux/actions/offerActions';
const googleMapURL =
'https://maps.googleapis.com/maps/api/js?libraries=places,geometry&key=AIzaSyA7XEFRxE4Lm28tAh44M_568fCLOP_On3k';
const isJson = str => {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
};
const GeolocationGoogleMap = withScriptjs(
withGoogleMap(props => (
<GoogleMap defaultZoom={6} zoom={props.zoom} center={props.center} onClick={props.onMapClick}>
{props.center && (
<Marker
position={props.center}
title={"User's Location"}
options={{ icon: require('./assets/blueDot.png') }}
>
{props.showCenterInfo && (
<InfoWindow>
<div>User's Location</div>
</InfoWindow>
)}
</Marker>
)}
{/* <Circle
center={props.center}
radius={props.zoom}
options={{
fillColor: 'red',
fillOpacity: 0.2,
strokeColor: 'red',
strokeOpacity: 1,
strokeWeight: 1,
}}
/> */}
{props.markers.map((marker, index) => {
const onClick = () => props.onMarkerClick(marker);
const onCloseClick = () => props.onCloseClick(marker);
return (
<Marker
key={index}
position={marker.position}
title={marker.number.toString()}
onClick={onClick}
options={{ icon: 'https://image.ibb.co/evMHxF/shopping_zone_marker_1.png' }}
>
{marker.showInfo && (
<InfoWindow onCloseClick={onCloseClick}>
<div>
<ItemList marker={marker} />
</div>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
)),
);
class OfferMap extends Component {
constructor(props) {
super(props);
this.state = {
currentPosition: null,
center: null,
content: null,
radius: 15, // ACZ --> put this const in config_env.
showCenterInfo: true,
markers: [],
zoom: 6,
bounds: null,
};
this.handleMarkerClick = this.handleMarkerClick.bind(this);
this.handleCloseClick = this.handleCloseClick.bind(this);
this.handleMapClick = this.handleMapClick.bind(this);
this.filterItemsByRadius = this.filterItemsByRadius.bind(this);
this.radiusChange = this.radiusChange.bind(this);
this.zoomChange = this.zoomChange.bind(this);
}
componentWillReceiveProps(props) {
if (props.coords && !props.coords.positionError) {
this.setState({ center: { lat: props.coords.latitude, lng: props.coords.longitude } });
} else {
fetch('http://ip-api.com/json')
.then(res => res.json())
.then(data => {
this.setState({ center: { lat: data.lat, lng: data.lon } });
})
.catch(error => {
this.setState({ content: `Error: The Geolocation service failed (${error.message}).` });
});
}
this.setState({ markers: props.items });
}
handleMapClick() {
this.setState({ showCenterInfo: false });
}
handleMarkerClick(targetMarker) {
this.setState({
markers: this.state.markers.map(marker => {
if (marker._id === targetMarker._id) {
return {
...marker,
showInfo: true,
};
}
return marker;
}),
});
}
handleCloseClick(targetMarker) {
this.setState({
markers: this.state.markers.map(marker => {
if (marker._id === targetMarker._id) {
return {
...marker,
showInfo: false,
};
}
return marker;
}),
});
}
filterItemsByRadius(userRadius) {
const items = this.state.markers;
const markers = [];
const bounds = new google.maps.LatLngBounds();
items.map((item, i) => {
let itemGeolocation;
let itemDescription = 'NO DESCRIPTION';
let itemThumb;
// Should be ready - tbaustinedit
if (item) {
itemDescription = item.description;
itemThumb = item.media.mediaVault[item.media.defaultIdx] || {
mediaType: 'txt',
};
}
if (item.geolocation) {
itemGeolocation = item.geolocation.coords;
}
if (this.state.center) {
const currentLocation = {
latitude: this.state.center.lat,
longitude: this.state.center.lng,
};
const distanceArr = geolib.orderByDistance(currentLocation, [itemGeolocation]);
const miles = (distanceArr[0].distance / 1609.34).toFixed(2);
if (miles <= userRadius) {
const loc = new google.maps.LatLng(itemGeolocation.lat, itemGeolocation.lng);
bounds.extends(loc);
markers.push({
_id: item._id,
position: itemGeolocation,
number: i,
content: itemDescription,
price: item.price,
quantity: item.quantity,
currency: item.currency,
category: item.category,
title: item.title,
offer: item.offer,
thumbnail: itemThumb,
showInfo: item.showInfo || false,
});
}
}
});
this.setState({
bounds,
});
return markers;
}
radiusChange(event) {
console.log(event.target.value);
this.setState({
radius: event.target.value,
});
const { filter } = this.props.offers;
filter.radius = event.target.value;
this.props.sortOffersNew(filter);
}
zoomChange(e) {
console.log('value', e.target.value);
this.setState({ zoom: Number(e.target.value) });
}
render() {
const markers = this.filterItemsByRadius(this.state.radius);
return (
<Col xs={12} smOffset={0} mdOffset={0}>
<div>
<div style={{ fontFamily: 'Roboto', fontStyle: 'normal' }}>
Offers within radius of: {' '}
<input type="text" defaultValue={this.state.radius} onChange={this.radiusChange} /> {' '}
miles
<br />
</div>
{/* <CreateRadius
radiusChange={this.radiusChange}
numOffers={markers.length}
initRadius={this.state.zoom}
/> */}
</div>
<br />
<div
style={{
width: '100%',
height: '500px',
}}
>
<GeolocationGoogleMap
googleMapURL={googleMapURL}
loadingElement={<div style={{ height: '100%' }} />}
containerElement={<div style={{ height: '100%' }} />}
mapElement={<div style={{ height: '100%' }} />}
center={this.state.center}
showCenterInfo={this.state.showCenterInfo}
content={this.state.content}
radius={this.state.radius}
onMapClick={this.handleMapClick}
onMarkerClick={this.handleMarkerClick}
onCloseClick={this.handleCloseClick}
markers={markers}
zoom={this.state.zoom}
bounds={this.state.bounds}
/>
</div>
</Col>
);
}
}
function mapStateToProps({ browser, offers }) {
return { browser, offers };
}
const dispatchToProps = dispatch => ({
sortOffersNew: filter => dispatch(offerActions.sortOffers(filter)),
});
export default connect(mapStateToProps, dispatchToProps)(
geolocated({
positionOptions: {
enableHighAccuracy: false,
},
userDecisionTimeout: 5000,
})(OfferMap),
);
You can access fitBounds by adding a ref to the DOM element that google-maps-react generates.
first, add a ref:
<GoogleMap ref={(ref) => { this.map = ref; }}>...</GoogleMap>
Once the component is mounted, you will be able to call fitBounds using the ref.
this.map.fitBounds(bounds)
You can assign a ref to the GoogleMap Component, then call the fitBounds() function using a lifecycle hook.
import React, { useRef, useEffect } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import { GOOGLE_API_KEY } from '../../config/config';
// =======================================================================
// GOOGLE MAPS
// =======================================================================
const RegularMap = withScriptjs(
withGoogleMap(
({ defaultCenter, markers }) => {
const mapRef = useRef(null);
// Fit bounds function
const fitBounds = () => {
const bounds = new window.google.maps.LatLngBounds();
markers.map(item => {
bounds.extend(item.position);
return item.id
});
mapRef.current.fitBounds(bounds);
};
// Fit bounds on mount, and when the markers change
useEffect(() => {
fitBounds();
}, [markers]);
return (
<GoogleMap ref={mapRef} defaultCenter={defaultCenter}>
{markers.map(
({ position }, index) => <Marker key={`marker_${index}`} position={position} />
)}
</GoogleMap>
);
})
);
// =======================================================================
// THE MAIN COMPONENT
// =======================================================================
const MapView = () => {
const markers = [
{ position: { lat: 37.778519, lng: -122.405640 } },
{ position: { lat: 6.4454594, lng: 3.449074 } }
];
return (
<RegularMap
defaultCenter={{ lat: 6.4454594, lng: 3.449074 }}
markers={markers}
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}`}
loadingElement={<div className="loader" />}
containerElement={<div className="mapContainer" style={{ height: "400px" }} />}
mapElement={<div className="map" style={{ height: '100%' }} />}
/>
);
}
export default MapView;
You can use fitbounds in componentDidMount or render function
add ref to GoogleMap and apply fitbounds
<GoogleMap ref={map => map && map.fitBounds(bounds)}> .... </GoogleMap>
The example demonstrates how to center viewport for the given markers:
const MapWithAMarkers = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
lifecycle({
componentWillMount() {
this.setState({
zoomToMarkers: map => {
//console.log("Zoom to markers");
const bounds = new window.google.maps.LatLngBounds();
map.props.children.forEach((child) => {
if (child.type === Marker) {
bounds.extend(new window.google.maps.LatLng(child.props.position.lat, child.props.position.lng));
}
})
map.fitBounds(bounds);
}
})
},
}),
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap ref={props.zoomToMarkers} defaultZoom={5} defaultCenter={{ lat: 25.0391667, lng: 121.525 }}>
{props.markers.map(marker => (
<Marker
key={marker.id}
position={{ lat: marker.lat, lng: marker.lng }}
/>
))}
</GoogleMap>
);
Demo
I wanted to restrict my map (https://developers.google.com/maps/documentation/javascript/examples/control-bounds-restriction).
I used the options property with GoogleMap component with https://www.npmjs.com/package/#react-google-maps/api library.
The library is complete re-write of the react-google-maps library
CODE:
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={10}
ref={(ref) => {this.state.map = ref}}
onLoad={this.onGoogleMapLoad}
options={{
restriction: {
latLngBounds: {
north: 19.137384, // Mumbai
south: 18.510866, // Pune
west: 72.874748, // Mumbai
east: 73.879864, // Pune
},
strictBounds: false,
}
}}
>
Related
I really don't understand why two infowindows appear after clicking a marker, the weird thing is sometimes in inspection mode infowindow appear normally. I'm wondering if the callback cause the problem. Code are below:
import { GoogleMap, Marker,withGoogleMap,withScriptjs, InfoWindow } from "react-google-maps";
import { nanoid } from 'nanoid'
import React, { Component } from 'react'
const API_KEY = 'INSERT_API_KEY'
const MyMapComponent = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: -33.897, lng: 151.144 }}
>
{props.locs.map((location)=>{
const onMarkerClick = props.onMarkerClick.bind(this,location)
return <Marker
key={nanoid()}
position={location}
onClick={onMarkerClick}>
</Marker>
})}
{props.showingInfoWindow &&
<InfoWindow position={props.activeMarker} onCloseClick={props.markerInfoClose}>
<h1>Details</h1>
</InfoWindow>}
</GoogleMap>
));
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
locations:[{lat: -33.865143,lng: 151.2},],
showingInfoWindow: false,
activeMarker: {},
};
}
onMarkerClick = (location) =>{
this.setState({
activeMarker: location,
showingInfoWindow: true
});
}
onClose = () => {
if (this.state.showingInfoWindow) {
this.setState({
activeMarker: null,
showingInfoWindow: false
});
}
};
render() {
console.log(this.state.activeMarker)
return (
<div>
<MyMapComponent
locs={this.state.locations}
onMarkerClick={this.onMarkerClick}
showingInfoWindow={this.state.showingInfoWindow}
activeMarker={this.state.activeMarker}
markerInfoClose={this.onClose}
containerElement={ <div style={{ height: `1000px`, width: '1000px' }} /> }
mapElement={ <div style={{ height: `100%` }} /> }
loadingElement={<div style={{ height: `100%` }} />}
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${API_KEY}`}
>
</MyMapComponent>
</div>
)
}
}
ScreenShoot:
__ I have used withprops in react-google-maps. i have also used searchbox from react-google-maps my problem is that when onplacechanged event occure i want i am trying to update center in state but this.setState is not accesible in onplacechabged function. i want to access this.setState from RMSelectLocation class but it could'nt accesible. is there any other to imlement google map with autocomplete searchbox in reactjs __
import React, { Component, useEffect } from "react";
import { compose, withProps, lifecycle } from "recompose";
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
} from "react-google-maps";
import { get } from "lodash";
import { SearchBox } from "react-google-maps/lib/components/places/SearchBox";
var cntr = { lat: 19.993982, lng: 73.790416 };
class RMSelectLocation extends Component {
state = {
locations: "nashik",
center: { lat: 19.993982, lng: 73.790416 },
zoom: 12,
type: "",
};
componentDidMount() {}
render() {
const MapWithASearchBox = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=AIzaSyAIVf-f5cukgHjy_ZEv32yc9Liehb4vTGQ&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `200%` }} />,
}),
lifecycle({
componentWillMount() {
const refs = {};
this.setState({
bounds: null,
center: {
lat: 19.9975,
lng: 73.7898,
},
markers: [],
onMapMounted: (ref) => {
refs.map = ref;
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
});
},
onSearchBoxMounted: (ref) => {
refs.searchBox = ref;
},
onPlacesChanged: () => {
console.log(this.state);
console.log(this.props);
const places = refs.searchBox.getPlaces();
const bounds = new window.google.maps.LatLngBounds();
places.forEach((place) => {
if (place.geometry.viewport) {
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
const nextMarkers = places.map((place) => ({
position: place.geometry.location,
}));
const nextCenter = get(
nextMarkers,
"0.position",
this.state.center
);
this.setState({
center: nextCenter,
markers: nextMarkers,
});
},
});
},
}),
withScriptjs,
withGoogleMap
)((props) => (
<GoogleMap
ref={props.onMapMounted}
defaultZoom={15}
center={props.center}
onBoundsChanged={props.onBoundsChanged}
defaultMapTypeId={"hybrid"}
>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={window.google.maps.ControlPosition.TOP_RIGHT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
{props.markers.map((marker, index) => (
<Marker key={index} position={marker.position} />
))}
</GoogleMap>
));
return (
<MapWithASearchBox />
);
}
}
export default RMSelectLocation;
Ciao, if this.setState is not reachable could be a this context problem as explained here. Take a look and let us know :)
I'm drawing custom polygon with DrawingManager.
I would like of modify fillColor of polygon with props. What should I do?
import React from 'react'
import GoogleMap from 'google-map-react'
export default (props: any) => {
let {color} = props
const handleGoogleMapApi = (google: any) => {
console.log(google)
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['polygon'],
},
polygonOptions: {
strokeWeight: 5,
fillOpacity: 0.45,
fillColor: color,
},
})
google.maps.event.addListener(drawingManager, 'polygoncomplete', (polygon: any) => {
let points: any = polygon.getPath().getArray()
points.forEach((point: any) => {
const {lat, lng} = point
console.log(lat(), lng())
})
})
google.map.setMapTypeId('satellite')
drawingManager.setMap(google.map)
}
return (
<div style={{height: '60%', width: '100%'}}>
<GoogleMap
defaultCenter={{lat: -19.810968444640704, lng: -43.54377112158203}}
defaultZoom={15}
yesIWantToUseGoogleMapApiInternals
onGoogleApiLoaded={handleGoogleMapApi}
onChange={handleGoogleMapApi}
bootstrapURLKeys={{libraries: 'drawing', key: `${process.env.REACT_APP_PROVIDER_GOOGLE}`}}
/>
</div>
)
}
I have a lot of experience working with GIS + React, but never work with google API. I'd dig into your problem and there's suggestion:
Probably you've chosen wrong library. I've read source code, examples of "google-map-react" - it's destitute maintained.
There are more popular alternatives such a https://github.com/fullstackreact/google-maps-react#polygon
Even from example you'll get how to change color of Polygon.
render() {
const center = this.getvalue();
const {area=[]} = this.props;
console.log(this.props);
return (
<googlemap
defaultzoom={14}
center={center}
onrightclick={this.erasepolygon}>
{
this.state.bounds.length != 0 &&
<polygon
paths={this.state.bounds}
options={{
strokecolor:"#d34052",
fillcolor:"#d34052",
strokeopacity:"0.5",
strokeweight:'2'
}}
/>
}
{
<drawingmanager
drawingmode={google.maps.drawing.overlaytype.polygon}
onpolygoncomplete={this.handleoverlay}
defaultoptions={{
drawingcontrol: true,
drawingcontroloptions: {
position: google.maps.controlposition.top_center,
drawingmodes:['polygon']
},
polygonoptions: {
fillcolor: `#ffffff`,
fillopacity: 0.7,
strokeweight: 1,
clickable: true,
editable: true,
zindex: 1
}
}}
/>
}
<marker
position={center}
/>
{
area.map((value, index) => {
return (
<polygon
key={index}
paths={value.bounds}
options={{
strokecolor:"#e34052",
fillcolor:"#e3352",
strokeopacity:"0.5",
strokeweight:'2'
}}
/>
)
})
}
</googlemap>
)
}
}
I'm a fresh starter in React and Apollo (GraphQL). I need to display a Layer when a user clicks on a google maps marker. My Apollo Request is displaying correctly the markers depending on found missions in the database, the only problem I'm facing is that the onClick is returning a TypeError: _this.handleMissionClick is not a function. I think this is because the function is outside the render part and the main class, and can't find a way to link the two parts.
Here is the complete page code :
// #flow
import React from 'react'
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
import { connect } from 'react-redux'
import Layer from 'grommet/components/Layer'
import MissionDetails from './../missions/details'
type TMission = {
id : string,
description: string,
status: string,
title: string,
location: number[]
}
type TMissionProps = {
data: {
loading: boolean,
searchMissions?: Array<TMission>
}
}
function setIcon (status) {
switch(status) {
case 'accepted':
return 'http://maps.google.com/mapfiles/ms/icons/green-dot.png';
case 'created':
return 'http://maps.google.com/mapfiles/ms/icons/red-dot.png';
default:
return null;
}
}
const _MissionList = (props: TMissionProps) => {
if (!props.data.searchMissions) return null
return (
props.data.searchMissions &&
props.data.searchMissions.map( mission => (
<Marker
position={{ lng: mission.location[0], lat: mission.location[1]}}
key={ mission.id }
title={ mission.title }
icon={setIcon(mission.status)}
onClick={() => this.handleMissionClick(mission.id)}
>
</Marker>
))
)
}
const MissionList = connect(
({ session }) => ({ t: 1 })
)(graphql(gql`
query mapMissions(
$authToken: String!
) {
searchMissions(
input: {
location: [2, 3]
}
) {
id
title
status
}
}
`, {
options: {
fetchPolicy: 'network-only'
}
})(_MissionList))
const GoogleMapWrapper = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={11}
defaultCenter={{ lat: 48.8588377, lng: 2.2770201 }}
center={props.center}
>
<MissionList/>
</GoogleMap>))
export default class DashboardPage extends React.Component {
constructor () {
super();
this.state = {
localised: {
lat: 48.8588377,
lng: 2.2770201,
selectedMissionId: null,
showMissionDetailsLayer: false
}
};
}
toggleMissionDetailsLayer = () => {
this.setState({
showMissionDetailsLayer: !this.state.showMissionDetailsLayer
})
}
componentDidMount () {
this.getLocation();
}
handleMissionClick = (missionId: string) => {
this.setState({
selectedMissionId: missionId
})
this.toggleMissionDetailsLayer()
}
getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
this.setState({
localised: {
lat: position.coords.latitude,
lng: position.coords.longitude
}}
)
})
}
}
render () {
return (
<div>
<div style={{ height: '20vh', width: '100%' }}>
</div>
<GoogleMapWrapper isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=$$$&?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `80vh` }} />}
center={{ lat : this.state.localised.lat, lng : this.state.localised.lng }}
/>
{
this.state.showMissionDetailsLayer &&
<Layer align='right' closer={true} overlayClose={true} onClose={this.toggleMissionDetailsLayer}>
<MissionDetails mission={_(this.props.data.allMissions).find({id: this.state.selectedMissionId})} />
</Layer>
}
</div>
)
}
}
Since your handleMissionClick is defined in DashboardPage. Which is one of the parents of MissionList you need to pass the callback handler through the props. That way MissionList has a prop to handle the onClick
In your DashboardPage you should pass an onClickHandler
<GoogleMapWrapper
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=$$$&?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `80vh` }} />}
center={{ lat : this.state.localised.lat, lng : this.state.localised.lng }}
handleMissionClick={this.handleMissionClick} // Add callback prop here
/>
Inside your GoogleMapWrapper pass the handleMissionClick prop to MissionList
const GoogleMapWrapper = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={11}
defaultCenter={{ lat: 48.8588377, lng: 2.2770201 }}
center={props.center}
>
<MissionList handleMissionClick={props.handleMissionClick} />
</GoogleMap>))
In your MissionProps you now have onClick as a prop
type TMissionProps = {
data: {
loading: boolean,
searchMissions?: Array<TMission>
},
onClick: string => void
}
Update your Marker to use the callback prop
<Marker
position={{ lng: mission.location[0], lat: mission.location[1]}}
key={ mission.id }
title={ mission.title }
icon={setIcon(mission.status)}
onClick={() => props.handleMissionClick(mission.id)} // Notice this is now props.handleMissionClick instead of this.handleMissionClick
>
</Marker>
I am trying to update the viewport bounds based on the markers coords in my react google maps component.
However when I call the following line in componentWillMount()
const bounds = new google.maps.LatLngBounds();
I get an error that says google is not defined.
To resolve this I have tried
(1) Adding the google maps script tag to the index.html
(2) Adding the line
/* eslint-disable no-undef */
to the top of my file.
(3) Adding withScriptjs, withGoogleMap inside my compose() but it produces an error that says the googleMapUrl and the loading element is not defined. To solve this I tried doing
<MapSearchBox
googleMapURL="https://maps.googleapis.com/maps/api/js?key=APIKEY&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
isMarkerShown={this.state.isMarkerShown}
onMarkerClick={this.handleMarkerClick}
/>
but this did not work.
(4) Added /* global google */ to the top of my file
Also some other small changes but nothing sticks.
Please give some suggestions!
MapWithASearchBox.js
/* global google */
import React from 'react';
import { get } from 'lodash';
import { compose, withProps, lifecycle, defaultProps } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
import PropTypes from 'prop-types';
const { SearchBox } = require('react-google-maps/lib/components/places/SearchBox');
import { buildMarkerObj } from '../../../imports/helpers/DataHelpers';
const MapSearchBox = compose(
withProps(props => ({
googleMapURL: 'https://maps.googleapis.com/maps/api/js?key=APIKEY=3.exp&libraries=geometry,drawing,places',
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `450px` }} />,
mapElement: <div style={{ height: `100%` }} />,
})),
withScriptjs,
withGoogleMap,
defaultProps({
externalMarkers: [],
}),
lifecycle({
componentWillMount() {
const refs = {};
const { lat, lng } = this.props.coords || (this.props.place && this.props.place.coords) || {};
const initialMarker = lat !== undefined && lng !== undefined ? [buildMarkerObj({ lat, lng })] : [];
console.log('THIS PROPS');
console.log(this.props);
console.log('PROPS');
this.setState({
bounds: null,
center: {
lat: lat || 41.9,
lng: lng || -87.624,
},
markers: initialMarker,
injectedMarkers: this.props.markers || [],
onMapMounted: ref => {
refs.map = ref;
},
onBoundsChanged: () => {
},
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
places.map(({ address_components, geometry: { location } }) => {
this.props.onSetLocation({
lat: location.lat(),
lng: location.lng(),
});
});
const nextMarkers = places.map(place => ({
position: place.geometry.location,
}));
const nextCenter = get(nextMarkers, '0.position', this.state.center);
this.setState({
center: nextCenter,
markers: nextMarkers,
});
// refs.map.fitBounds(bounds);
},
})
//ERROR HERE
const bounds = new google.maps.LatLngBounds();
this.props.markers.map((marker, index) => {
bounds.extend(new google.maps.LatLng(
marker.coords.lat,
marker.coords.lng
));
})
refs.map.fitBounds(bounds);
refs.map.panToBounds(bounds);
},
}),
)
((props) =>
<GoogleMap
ref={props.onMapMounted}
defaultZoom={15}
center={props.center}
onBoundsChanged={props.onBoundsChanged}
>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Enter your area"
style={{
boxSizing: 'border-box',
border: '1px solid white',
width: '240px',
height: '32px',
marginTop: '12px',
padding: '0 12px',
borderRadius: '3px',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.3)',
fontSize: '14px',
outline: 'none',
textOverflow: 'ellipses',
backgroundColor: 'white',
}}
/>
</SearchBox>
{props.markers.map((marker, index) =>
<Marker key={`map-marker-${index}`} position={marker.position} />
)}
{props.externalMarkers.map((marker, index) =>
<Marker key={`external-marker-${index}`} position={marker.coords} />
)}
</GoogleMap>
)
class MapComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = { isMarkerShown: false };
}
componentDidMount() {
this.delayedShowMarker();
}
componentDidUpdate() {
console.log('COMPONENT DID UPDATE');
const bounds = new window.google.maps.LatLngBounds();
this.props.markers.map((marker, index) => {
bounds.extend(new window.google.maps.LatLng(
marker.coords.lat,
marker.coords.lng
));
})
refs.map.fitBounds(bounds);
refs.map.panToBounds(bounds);
}
delayedShowMarker = () => {
setTimeout(() => {
this.setState({ isMarkerShown: true });
}, 3000);
}
handleMarkerClick = () => {
this.setState({ isMarkerShown: false });
this.delayedShowMarker();
}
render() {
return (
<MapSearchBox
isMarkerShown={this.state.isMarkerShown}
onMarkerClick={this.handleMarkerClick}
/>
);
}
}
MapComponent.propTypes = {
className: PropTypes.string,
latitude: PropTypes.string,
longitude: PropTypes.string,
externalMarkers: PropTypes.array,
};
MapComponent.defaultProps = {
className: '',
externalMarkers: [],
};
export default MapSearchBox;
Add Google Map url script in index.html
<script src="https://maps.googleapis.com/maps/api/js?key=APIKEY=3.exp&libraries=geometry,drawing,places></script>