Michael Lee Michael Lee -4 years ago 189
React JSX Question

getPlace function not working with Google Autocomplete in react

I'm trying to recreate this Google autocomplete example in react: https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform

I am using this tutorial to generate the higher order component needed to properly load the Google API: https://www.fullstackreact.com/articles/how-to-write-a-google-maps-react-component/

The autocomplete functionality seems to be working just fine. However, when I select a suggestion and try to run

fillInAddress()
this is the error message that I get:

Uncaught TypeError: Cannot read property 'getPlace' of undefined


This is my code
/components/GoogleApiComponent.jsx

import React from 'react';
import {GoogleApiWrapper} from 'google-maps-react';
import GoogleAutoComplete from '../components/GoogleAutoComplete';


export class Container extends React.Component {
render() {
if (!this.props.loaded) {
return <div>Loading...</div>
}
return (
<div>
<GoogleAutoComplete
/>
</div>

)
}
}

export default GoogleApiWrapper({
apiKey: 'somekey'
})(Container)


../components/GoogleAutoComplete.jsx

import React from 'react';

export default class GoogleAutoComplete extends React.Component {
static propTypes = {
}
constructor(props) {
super(props);
}

componentDidMount() {
this.initAutocomplete();
}

initAutocomplete() {
this.autocomplete = new google.maps.places.Autocomplete((this.refs.autoCompletePlaces), {types: ['geocode']});

this.autocomplete.addListener('place_changed', this.fillInAddress);


}

geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
const geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
});
}
}

fillInAddress() {

const componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
// Get the place details from the autocomplete object.
const place = this.autocomplete.getPlace();
for (let component in componentForm) {
this.refs.component.value = '';
this.refs.component.disabled = false;
}

// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (let i = 0; i < place.address_components.length; i++) {
const addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
const val = place.address_components[i][componentForm[addressType]];
this.refs.addressType.value = val;
}
}
}

render() {
return (
<div>
<div>
<input
placeholder="Enter your address"
onFocus={this.geolocate}
ref="autoCompletePlaces"
/>
</div>
<table ref="address">
<tbody>
<tr>
<td>Street address</td>
<td>
<input
ref="street_number"
disabled="true"/>
</td>
<td>
<input
ref="route"
disabled="true"/>
</td>
</tr>
<tr>
<td>City</td>
<td>
<input
ref="locality"
disabled="true"/>
</td>
</tr>
<tr>
<td>State</td>
<td>
<input
ref="administrative_area_level_1"
disabled="true"/>
</td>
<td>Zip code</td>
<td>
<input
ref="postal_code"
disabled="true"/>
</td>
</tr>
<tr>
<td>Country</td>
<td>
<input
ref="country"
disabled="true"/>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}

Answer Source

I figured this out, with some help of the people over at discord, and this other stackoverflow question. As per that answer, the key point is "Inside the place_changed-callback the keyword this points to the object which has been triggered the event.". I made that change and it worked.
This is the key line: this.place = this.autocomplete.getPlace();

For completeness I also re-factored the form fill this.place.address_components.forEach((component, index) => {

 const addressType = this.place.address_components[index].types[0];
      if (componentForm[addressType]) {
        const val = this.place.address_components[index][componentForm[addressType]];
        this.refs[addressType].value = val;
      }
    })

Code in its entirety is as follows:

import React from 'react';

export default class GoogleAutoComplete extends React.Component {
  static propTypes = {
    }
  constructor(props) {
    super(props);
      this.fillInAddress = this.fillInAddress.bind(this);

    }

    componentDidMount() {
      this.initAutocomplete();
    }


    initAutocomplete() {
      this.autocomplete = new google.maps.places.Autocomplete((this.refs.autoCompletePlaces), {types: ['geocode']});

      this.autocomplete.addListener('place_changed', this.fillInAddress);

    }

    geolocate() {
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
          const geolocation = {
            lat: position.coords.latitude,
            lng: position.coords.longitude
          };
        });
      }
    }

    fillInAddress() {
      const componentForm = {
        street_number: 'short_name',
        route: 'long_name',
        locality: 'long_name',
        administrative_area_level_1: 'short_name',
        country: 'long_name',
        postal_code: 'short_name'
      };
    // Get the place details from the autocomplete object.
      this.place = this.autocomplete.getPlace();
      /*this.setState({placeResult: this.place.address_components})*/

      for (let component in this.componentForm) {
        this.refs.component.value = '';
        this.refs.component.disabled = false;
      }

    // Get each component of the address from the place details
    // and fill the corresponding field on the form.

    this.place.address_components.forEach((component, index) => {
      const addressType = this.place.address_components[index].types[0];
      if (componentForm[addressType]) {
        const val = this.place.address_components[index][componentForm[addressType]];
        this.refs[addressType].value = val;
      }
    })   
  }    

  render() {
    return (
      <div>
        <div>
          <input 
            placeholder="Enter your address"
            onFocus={this.geolocate}
            ref="autoCompletePlaces"
            className="form-control"
            type="text"
          />
        </div>
        <table ref="address">
          <tbody>
            <tr>
              <td>Street address</td>
              <td>
                <input 
                  ref="street_number"
                  disabled="true"
                  value=''
                  />
              </td>
              <td>
                <input 
                  ref="route"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>City</td>
              <td>
                <input 
                  ref="locality"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>State</td>
              <td>
                <input 
                  ref="administrative_area_level_1" 
                  disabled="true"/>
                </td>
              <td>Zip code</td>
              <td>
                <input
                  ref="postal_code"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>Country</td>
              <td>
                <input
                  ref="country" 
                  disabled="true"/>
              </td>
            </tr>
          </tbody>
        </table>
      </div>      
    );
  }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download