Geocoder
Closed this issue ยท 13 comments
How can i use the new google.maps.Geocoder()
library?, i'm using the ScriptjsLoader to load map lib, and i need to search the address from my point.
This project uses geocode
. I adapted some of his code to my project (using a material-ui autocomplete box instead of his custom component), so the geocode
etc. happens outside of the react-google-maps
component, but so far both components are working harmoniously.
His code handles the UI and the API stuff all together, so it's a bit hard to grok.here's my simplified adaptation below (it combines autoComplete
with geocode
).
the general flow is:
- grab the stuff we need from
window
incomponentWillMount
- On user-input into our
TextField
, firesearchSuggests()
, which invokesautocompleteService
api - when user selects item, capture the
placeID
and pass it togeocode
to get the coordinates & return theplace
componentWillMount() {
if (typeof window === 'undefined') {
return
}
// grab our googleMaps obj from whever she may lay
var googleMaps = this.props.googleMaps ||
(window.google && // eslint-disable-line no-extra-parens
window.google.maps) ||
this.googleMaps
if (!googleMaps) {
console.error(// eslint-disable-line no-console
'Google map api was not found in the page.')
return
}
// now grab the services we need
this.googleMaps = googleMaps
this.autocompleteService = new googleMaps.places.AutocompleteService()
this.geocoder = new googleMaps.Geocoder()
}
// fire maps api call w/ current user input
searchSuggests() {
if (!this.state.userInput) {
this.updateSuggests()
return
}
// bias search for faster results
// TODO bias further
const options = {
input: this.state.userInput,
componentRestrictions: {
country: 'us'
}
}
// make it so
this.autocompleteService.getPlacePredictions(
options,
suggestsGoogle => {
// send our results to our update function
this.updateSuggests(suggestsGoogle || [])
}
)
}
// turn API call results to user state to populate autocomplete box
updateSuggests(suggestsGoogle = []) {
let suggests = []
let style = this.getStyles()
suggestsGoogle.forEach(suggest => {
let addressArr = suggest.description.split(',')
suggests.push({
placeID: suggest.place_id,
value: <MenuItem
primaryText={<div style={style.menuItem}>
{addressArr[0]}
<br />
<span style={style.menuItemSubtitle}>{addressArr.slice(1, -1).join(',') }</span>
<Divider style={style.menuItemDivider}/>
</div>
}
/>,
address: suggest.description
})
})
this.setState({ suggests })
}
onInputChange = userInput => {
this.setState({ userInput }, this.searchSuggests)
}
// when user chooses result
handleSuggestSelect = (suggestPlaceID) => {
// user picks one, but so far we only have address & place IDS,
// so we need to get coords to plot on map
this.geocoder.geocode(
{ placeId: suggestPlaceID.placeID },
(results, status) => {
if (status !== this.googleMaps.GeocoderStatus.OK) {
return
}
const location = results[0].geometry.location
const suggestLocation = {
address: suggestPlaceID.address,
coordinates: {
lat: location.lat(),
lng: location.lng()
},
googlePlaceID: suggestPlaceID.placeID
}
this.props.handlePlaceSelect(suggestLocation)
}
)
}
@thiagoterleski what @brandonmp did seems okay. react-google-maps
doesn't wrap the Geocoder
object for you. You'll need to connect yourself.
Also, 6.0.0 is released on npm beta tag now. We also have a new demo page. Feel free to try it:
https://tomchentw.github.io/react-google-maps/
Is there any chance we can add this geocoder in react-google-maps? @tomchentw
You're free to submit a pull request anytime!
As I can tell, to use Geocode you don't need it to be a React Compnenent. You can simply use it in the react lifecycle callbacks
Would you mind giving a short hint how to use it in the react lifecycle callbacks? I'm struggling with the basics here, I think, and would highly appreciate help. Thanks for the good work!
@AntoniusGolly You can find Geocode object in window.google.maps
so for getting results from Geocode you need something like this:
let geocoder = new window.google.maps.Geocoder();
geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
if (status == 'OK') {
console.log(results);
} else {
console.log('Geocode was not successful for the following reason: ' + status);
}
});
@slivniy Thanks for the reply.
This works (supposingly) only if I had the google maps library sourced manually with <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY></script>
. But I would like to avoid putting a script tag in my document.
Alternatively, if use a stateless functional component (SFC) I can actually use new google.maps.Geocoder()
within withScriptJs(withGoogleMap( XX )) as in
import React from "react"
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"
const Map = withScriptjs(withGoogleMap((props) => {
let geocoder = new google.maps.Geocoder();
if (geocoder) {
geocoder.geocode( { 'address': props.address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
var coords = results[0].geometry.location.toJSON();
// this works fine
console.log(coords)
} else {
alert("No results found");
}
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
}))
export default Map
and instantiate it with
import Map from "../components/Map"
<Map
isMarkerShown
address = 'Bakerstreet, 2'
googleMapURL="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
However, <Map />
needs to return something to render it. In order to use the lifecycle callbacks as suggested by @tomchentw (I understand those as componentDidMount
, componentWillUnmount
, etc) I would need to write it as a stateful react component with Map extends React.component
.
In order to use the SFC I would need to do the geocoder stuff before the mounting of <Map />
and put <Map />
in the callback as you suggested. But there, google
is not defined. Any love?
Hmm, still doesn't work without sourcing google maps api manually. The error remains "Cannot read property 'maps' of undefined. Do you suggest to put the <script>
tag in the document?
@AntoniusGolly
Sorry, didn't checked before comment... Really, in the moment of ComponentDidMount
Google library isn't loaded.
I'm using Geocoder not right after component mounted, but on user click on some input - and in that time usually window.google
already exist.
If you need it really in componentDidMount
code can be next:
import React, { Component } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap } from 'react-google-maps';
import { compose, withProps, lifecycle } from 'recompose';
export default class MapRow extends Component {
render() {
const MyMapComponent = compose(
withProps({
googleMapURL: ('https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=' + API_KEY),
loadingElement: (<div style={{ height: '100%' }} />),
containerElement: (<div style={{ height: '400px' }} />),
mapElement: (<div style={{ height: '100%' }} />)
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
let geocoder = new window.google.maps.Geocoder();
geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
if (status == 'OK') {
console.log('here result of geocoder', results);
} else {
console.log('Geocode was not successful for the following reason: ' + status);
}
});
}
})
)(props =>
<GoogleMap
defaultZoom={7}
defaultCenter={new window.google.maps.LatLng(41.8507300, -87.6512600)}
>
</GoogleMap>
);
return(
<MyMapComponent/>
);
}
}
That's checked :)
Don't forget to change API_KEY
and if need googleMapURL
Taken with changes from: https://tomchentw.github.io/react-google-maps/#directionsrenderer
That's simplest way. Though I prefer to declare MyMapComponent variable in constructor
method - so when you will change state of that component it will not rerender map
.
Thank you, @slivniy !
I am trying to change the state value inside the componentDidMount().But it only accepts props values
@pranabunni If you change it to a Component or PureComponent you'll then have access to state. For example:
import React, { Component } from 'react'
import { withScriptjs, withGoogleMap, GoogleMap } from 'react-google-maps'
import { compose, withProps } from 'recompose'
class MapComponent extends Component {
componentDidMount() {
let geocoder = new window.google.maps.Geocoder();
geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
if (status == 'OK') {
this.setState({
lat: results[0].geometry.location.lat(),
lng: results[0].geometry.location.lng()
})
} else {
console.log('Geocode was not successful for the following reason:', status);
}
})
}
render() {
const { lat, lng } = this.state
return lat && lng ? <GoogleMap
defaultZoom={7}
defaultCenter={{ lat, lng }}
>
</GoogleMap> : <div>Loading...</div>
}
}
export default compose(
withProps({
googleMapURL: ('https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=' + API_KEY),
loadingElement: (<div style={{ height: '100%' }} />),
containerElement: (<div style={{ height: '400px' }} />),
mapElement: (<div style={{ height: '100%' }} />)
}),
withScriptjs,
withGoogleMap)(MapComponent)