Google Places 自动完成 React 组件

Posted

技术标签:

【中文标题】Google Places 自动完成 React 组件【英文标题】:Google Places Autocomplete React Component 【发布时间】:2017-07-10 01:25:18 【问题描述】:

我正在学习 react 并尝试创建一个 Google Places 自动完成组件,在 Rails 应用程序中使用 react on rails gem。这是我目前所拥有的,需要一些帮助来完成它。

我认为我在正确加载 Google javascript 库时遇到了问题。请参阅下面控制台的错误消息(注意我的应用程序中运行了正确的 [KEY])。

Navigated to https://react-on-rails-basic-tutorial-mcl282.c9users.io/
js?key=[KEY]&libraries=places&callback=initAutocomplete:102 Uncaught Yc message: "initAutocomplete is not a function", name: "InvalidValueError", stack: "Error↵    at new Yc (https://maps.googleapis.com/m…libraries=places&callback=initAutocomplete:139:73"
Yg @ js?key=[KEY]&libraries=places&callback=initAutocomplete:102
(anonymous) @ js?key=[KEY]&libraries=places&callback=initAutocomplete:139
google.maps.Load @ js?key=[KEY]&libraries=places&callback=initAutocomplete:21
(anonymous) @ js?key=[KEY]&libraries=places&callback=initAutocomplete:138
(anonymous) @ js?key=[KEY]&libraries=places&callback=initAutocomplete:139
createReactElement.js?74ab:40 RENDERED GoogleAutoComplete to dom node with id: GoogleAutoComplete-react-component-f9a3e037-df00-4f35-9a7c-103c33b1208e with props, railsContext: Object text: "google autocomplete" Object inMailer: false, i18nLocale: "en", i18nDefaultLocale: "en", href: "https://react-on-rails-basic-tutorial-mcl282.c9users.io/", location: "/"…
clientStartup.js?0ac5:149 Uncaught TypeError: ReactOnRails encountered an error while rendering component: GoogleAutoComplete.Original message: Cannot read property 'Autocomplete' of undefined
    at GoogleAutoComplete.initAutocomplete (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:3586), <anonymous>:44:48)
    at GoogleAutoComplete.componentDidMount (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:3586), <anonymous>:38:12)
    at eval (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2842), <anonymous>:265:25)
    at measureLifeCyclePerf (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2842), <anonymous>:75:12)
    at eval (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2842), <anonymous>:264:11)
    at CallbackQueue.notifyAll (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2296), <anonymous>:76:22)
    at ReactReconcileTransaction.close (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:3004), <anonymous>:80:26)
    at ReactReconcileTransaction.closeAll (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2398), <anonymous>:206:25)
    at ReactReconcileTransaction.perform (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:2398), <anonymous>:153:16)
    at batchedMountComponentIntoNode (eval at <anonymous> (webpack-bundle.self-cdcd22e….js?body=1:3130), <anonymous>:126:15)

以下是相关代码:

index.html.erb

<html>
  <body>
  <%= react_component("GoogleAutoComplete") %>

  <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAnhYQqHo2V5AcFpcKKPX6rz0bVrw7xmZg&libraries=places&callback=initAutocomplete"
        ></script>
  </body>
</html>

GoogleAutoComplete.jsx

import React,  PropTypes  from 'react';

export default class GoogleAutoComplete extends React.Component 
  static propTypes = 
    
  constructor(props) 

      super(props);
        this.state =  
          autocomplete:  
        
    

    componentDidMount() 
      this.initAutocomplete();
    

    initAutocomplete() 
      // eslint-disable-next-line no-undef
      const autocomplete = new google.maps.places.Autocomplete((this.refs.autoCompletePlaces), types: ['geocode']);

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

    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 = 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 id="locationField">
          <input 
            id="autocomplete" 
            placeholder="Enter your address"
            onFocus=this.geolocate
            onChange=this.handleInputChange
            ref="autoCompletePlaces"
          />
        </div>
        <table id="address">
          <tbody>
            <tr>
              <td>Street address</td>
              <td>
                <input 
                  id="street_number"
                  disabled="true"/>
              </td>
              <td>
                <input 
                  id="route"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>City</td>
              <td>
                <input 
                  id="locality"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>State</td>
              <td>
                <input 
                  id="administrative_area_level_1" 
                  disabled="true"/>
                </td>
              <td>Zip code</td>
              <td>
                <input
                  id="postal_code"
                  disabled="true"/>
              </td>
            </tr>
            <tr>
              <td>Country</td>
              <td>
                <input
                  id="country" 
                  disabled="true"/>
              </td>
            </tr>
          </tbody>
        </table>
      </div>      
    );
  

【问题讨论】:

嗨迈克尔,我知道这是一篇旧帖子,但在您的问题中的 html 脚本中有一个 API 密钥 - 可能值得编辑和删除它:)。如果你想用它作为一个反应钩子,我有一个帖子可以帮助创建/使用这个钩子和谷歌的地方自动完成服务:atomizedobjects.com/blog/react/… 【参考方案1】:

发生错误是因为initComplete() 在全局范围内不可用。 initComplete() 是 React 组件方法,不能在组件外调用。

要将callback 参数传递给Google Maps API,您需要异步加载库,而不是像以前那样使用&lt;script&gt; 标记。

请查看以下存储库,了解它们如何处理 Google 地图库的异步加载。

https://github.com/fullstackreact/google-maps-react

【讨论】:

我想我理解了这里的概念,需要在其中加载带有 Google Maps API 脚本的高阶组件,为组件提供我所需要的功能。但是,我真的不明白如何实现它。他们给出了这个代码示例:'import GoogleApiWrapper from 'GoogleMapsReactComponent' // ... export class Container extends React.Component export default GoogleApiWrapper( apiKey: GAPI_KEY )(Container)'如何使用我的 GoogleAutoComplete.jsx 组件实现这一点? @MichaelLee GoogleApiWrapper 组件将 loaded 属性传递给被包装的组件。仅在加载库后,此道具才设置为 true。当一个 prop 发生变化时,被包裹的组件会再次渲染自己,你可以在componentDidMount() 中检查loaded 是否为真,如果为真则调用initAutocomplete() 这行得通!真正让我感到困惑的一件事是博客有错误,请参阅此链接:github.com/fullstackreact/google-maps-react/issues/18; 我也在尝试这样做并理解概念上正在发生的事情,但不知道如何实现它 - ***.com/questions/46780770/… - 你有什么建议吗?看起来您链接到的库有一个特定于它的帮助程序,并且由于一段时间内 repo 上没有任何活动,我宁愿不包含它。

以上是关于Google Places 自动完成 React 组件的主要内容,如果未能解决你的问题,请参考以下文章

Google Places Api,放置自动完成入站查询

Google Places 自动完成的替代方案 [关闭]

如何为 Google Places API 自动完成文本框设置默认值

Google Places API Android:自动完成关闭太快

使用 Google PLACES Api 搜索查看自动完成建议

Google Places API,使用邮政编码自动完成