How to make google maps work with react - reactjs

I'm trying to Map with a SearchBox in react but I'm not sure what I am missing from this code below. I've never used google maps before and the docs are really confusing. The error I get is that google is no defined and I'm assuming I need to import something but research has proven to be useless. I got the code from https://github.com/tomchentw/react-google-maps.
const _ = require("lodash");
const { compose, withProps, lifecycle } = require("recompose");
const {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
} = require("react-google-maps");
const { SearchBox } = require("react-google-
maps/lib/components/places/SearchBox");
const MapWithASearchBox = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?
key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp&libraries
=geometry,draw
ing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
lifecycle({
componentWillMount() {
const refs = {}
this.setState({
bounds: null,
center: {
lat: 41.9, lng: -87.624
},
markers: [],
onMapMounted: ref => {
refs.map = ref;
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
})
},
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
const bounds = new 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,
});
// refs.map.fitBounds(bounds);
},
})
},
}),
withScriptjs,
withGoogleMap
)(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="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>
);
<MapWithASearchBox />

This error is caused by linter, you need to explicitly read google global variable from window by adding the below line to the top of your file:
const google = window.google;
or to disable linter:
/*global google*/

Related

React-Google-Maps API: How to search current location for a search result?

I'm trying to build a similar map as on Airbnb, where you can view place markers as you drag the map around. I would like to search for "treatment centers" and place markers using the Google Places API on a map.
I have been using the new, re-written #react-google-maps/api. So far, I was able to create both a search box and an autocomplete and get their latitude and longitude, but both offer only specific locations rather than the most similar searches (ex. if you search Taco Bell on Google Maps, it shows up with several options near you). The code below displays a map with the search box:
import { GoogleMap, LoadScript, Marker, StandaloneSearchBox, Autocomplete } from '#react-google-maps/api';
class HeaderMap extends Component {
constructor (props) {
super(props)
this.autocomplete = null
this.onLoad = this.onLoad.bind(this)
this.onPlaceChanged = this.onPlaceChanged.bind(this)
this.state = {
currentLocation: {lat: 0, lng: 0},
markers: [],
zoom: 8
}
}
componentDidMount() {
navigator?.geolocation.getCurrentPosition(({coords: {latitude: lat, longitude: lng}}) => {
const pos = {lat, lng}
this.setState({currentLocation: pos})
})
}
onLoad (autocomplete) {
console.log('autocomplete: ', autocomplete)
this.autocomplete = autocomplete
}
onPlaceChanged() {
if (this.autocomplete !== null) {
let lat = this.autocomplete.getPlace().geometry.location.lat()
let long = this.autocomplete.getPlace().geometry.location.lat()
} else {
console.log('Autocomplete is not loaded yet!')
}
}
render() {
return (
<LoadScript
googleMapsApiKey="API_KEY_HERE"
libraries={["places"]}
>
<GoogleMap
id='search-box-example'
mapContainerStyle={containerStyle}
center={this.state.currentLocation}
zoom={14}
// onDragEnd={search for centers in current location}
>
<Marker key={1} position={this.state.currentLocation} />
<Autocomplete
onLoad={this.onLoad}
onPlaceChanged={this.onPlaceChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
style={inputStyles}
/>
</Autocomplete>
</GoogleMap>
</LoadScript>
);
}
}
How can I automatically search the bounds of the location and get the latitude and longitude of each result based on keywords? Thanks for your help!
In your current code, it seems that you are using Autocomplete which was precoded by the library to have the functions of Places Autocomplete. You can use the StandaloneSearchBox to achieve your use case as it is implementing the Places Searchbox which returns a pick list that includes both places and predicted search terms.
Here is the code sample and code snippet below:
/*global google*/
import React from "react";
import { GoogleMap, StandaloneSearchBox, Marker } from "#react-google-maps/api";
let markerArray = [];
class Map extends React.Component {
state = {
currentLocation: { lat: 0, lng: 0 },
markers: [],
bounds: null
};
onMapLoad = map => {
navigator?.geolocation.getCurrentPosition(
({ coords: { latitude: lat, longitude: lng } }) => {
const pos = { lat, lng };
this.setState({ currentLocation: pos });
}
);
google.maps.event.addListener(map, "bounds_changed", () => {
console.log(map.getBounds());
this.setState({ bounds: map.getBounds() });
});
};
onSBLoad = ref => {
this.searchBox = ref;
};
onPlacesChanged = () => {
markerArray = [];
let results = this.searchBox.getPlaces();
for (let i = 0; i < results.length; i++) {
let place = results[i].geometry.location;
markerArray.push(place);
}
this.setState({ markers: markerArray });
console.log(markerArray);
};
render() {
return (
<div>
<div id="searchbox">
<StandaloneSearchBox
onLoad={this.onSBLoad}
onPlacesChanged={this.onPlacesChanged}
bounds={this.state.bounds}
>
<input
type="text"
placeholder="Customized your placeholder"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
position: "absolute",
left: "50%",
marginLeft: "-120px"
}}
/>
</StandaloneSearchBox>
</div>
<br />
<div>
<GoogleMap
center={this.state.currentLocation}
zoom={10}
onLoad={map => this.onMapLoad(map)}
mapContainerStyle={{ height: "400px", width: "800px" }}
>
{this.state.markers.map((mark, index) => (
<Marker key={index} position={mark} />
))}
</GoogleMap>
</div>
</div>
);
}
}
export default Map;

could not update state after onplacechanged event occure in react js?

__ 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 :)

How to update fillColor of polygonOptions with props?

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>
)
}
}

(google-maps-react) Material-UI popover detail bubble won't follow map marker when map centers to marker (LatLng)

I'm building a map with map markers that show a detail bubble built with the Material-UI Popover component. My code centers the marker when it is clicked, but the detail bubble/popover remains in the spot over where the map marker was before it was centered.
Here is a pic of the detail bubble/Popover when the map marker is centered:
I already tried positioning the detail bubble/popover as such:
.popover {
position: element(#map-marker);
transform: translateY(-100%);
}
But it still behaves the same. I think the popover component
can't calculate the change in the positioning of the map marker because the change is dictated by lat/lng values for the center of the map. I just can't think of any way to circumvent this.
Here is the full code:
Map.js
class ShowsMap extends Component {
constructor(props) {
super(props);
this.state = {
detailBubbleAnchorEl: null // The currently selected marker that the popover anchors to
}
}
handleDetailClose = () => {
this.setState({
detailBubbleAnchorEl: null
})
}
handleMarkerClick = (event, lat, lng) => {
this.setState({
detailBubbleAnchorEl: event.currentTarget
})
// Set center coordinates of map to be those of the map marker (redux action)
this.props.setSearchCenter({ lat, lng })
}
renderMap = () => {
const { detailBubbleAnchorEl } = this.state;
const detailOpen = Boolean(detailBubbleAnchorEl);
const { viewport } = this.props.searchLocation;
const { zoom } = fitBounds(viewport, { width: 400, height: 600})
return (
<GoogleMapReact
yesIWantToUseGoogleMapApiInternals
bootstrapURLKeys={{ key: MYAPIKEY }}
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
zoom={zoom + 1}
center={this.props.searchLocation.center}
onGoogleApiLoaded={({ map, maps }) => this.handleApiLoaded(map, maps)}
>
{
showLocations.map((location, index) => {
const { lat, lng } = location;
return (
<div lat={lat} lng={lng} key={index}>
<MapMarker onClick={(event) => this.handleMarkerClick(event, lat, lng)} />
<DetailBubble
id="event"
open={detailOpen}
anchorEl={detailBubbleAnchorEl}
onClose={this.handleDetailClose}
/>
</div>
)
})
}
</GoogleMapReact>
)
}
render() {
return (
<div ref={map => this.map = map} style={{ width: '100%', height: '100%',}}>
{this.renderMap()}
</div>
);
}
DetailBubble.js
const DetailBubble = ({ classes, open, anchorEl, onClose, id }) => {
return(
<Popover
id={id}
classes={{ paper: classes.container}}
open={open}
anchorEl={anchorEl}
onClose={onClose}
anchorOrigin={{
vertical: 'top',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
>
</Popover>
)
}
const styles = theme => ({
container: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
width: '200px',
height: '150px'
}
});
MapMarker.js
const styles = theme => ({
markerContainer: {
position: 'absolute',
width: 35,
height: 35,
left: -35 / 2,
top: -35 / 2,
},
marker: {
fill: '#3f51b5',
'&:hover': {
fill: 'blue',
cursor: 'pointer'
}
}
})
function MapMarker({ classes, onClick }) {
return (
<div className={classes.markerContainer}>
<Marker onClick={onClick} className={classes.marker} width={30} height={30} />
</div>
)
}
Thanks in advance for your help!

React google maps url undefined

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>

Resources