React/Material UI - Google Places 自动完成下拉菜单有时不起作用

Posted

技术标签:

【中文标题】React/Material UI - Google Places 自动完成下拉菜单有时不起作用【英文标题】:React/Material UI - Google Places autocomplete dropdown sometimes doesn't work 【发布时间】:2022-01-22 17:58:39 【问题描述】:

所以这段代码可以工作,但有时在页面加载或刷新谷歌地方下拉自动完成建议时没有显示,我可以在搜索框中输入任何内容,但它只是不加载自动完成框。什么可能导致此问题?

这是代码。

const apiKey = process.env.REACT_APP_GOOGLE_KEY;
const mapApiJs = 'https://maps.googleapis.com/maps/api/js';
const geocodeJson = 'https://maps.googleapis.com/maps/api/geocode/json';


// load google map api js

function loadAsyncScript(src) 
  return new Promise(resolve => 
    const script = document.createElement("script");
    Object.assign(script, 
      type: "text/javascript",
      async: true,
      src
    )
    script.addEventListener("load", () => resolve(script));
    document.head.appendChild(script);
  )


const extractAddress = (place) => 

  const address = 
    city: "",
    state: "",
    zip: "",
    country: "",
    plain() 
        const city = this.city ? this.city + ", " : "";
        const zip = this.zip ? this.zip + ", " : "";
        const state = this.state ? this.state + ", " : "";
        return city + zip + state + this.country;
    
  

  if (!Array.isArray(place?.address_components)) 
    return address;
  

  if(typeof place.geometry.location.lat === 'function') 
    address.lat = place.geometry.location.lat();
   else 
    address.lat = place.geometry.location.lat;
  

  if(typeof place.geometry.location.lng === 'function') 
    address.lng = place.geometry.location.lng();
   else 
    address.lng = place.geometry.location.lng;
  

  place.address_components.forEach(component => 
    const types = component.types;
    const value = component.long_name;

    if (types.includes("locality")) 
      address.city = value;
    

    if (types.includes("administrative_area_level_2")) 
      address.state = value;
    

    if (types.includes("postal_code")) 
      address.zip = value;
    

    if (types.includes("country")) 
      address.country = value;
    

  );

  return address;



function ReportForm() 

    const searchInput = useRef(null);
    const [address, setAddress] = useState();

    const [coords, setCoords] = useState(
        lat: 42.680115925419294,
        lng: 14.010667884881462,
    );

    // init gmap script
    const initMapScript = () => 
        // if script already loaded
        if(window.google) 
          return Promise.resolve();
        
        const src = `$mapApiJs?key=$apiKey&libraries=places&v=weekly`;
        return loadAsyncScript(src);
    

    // do something on address change
    const onChangeAddress = (autocomplete) => 
        const place = autocomplete.getPlace();
        setAddress(extractAddress(place));
        setCoords(
            lat: place.geometry.location.lat(),
            lng: place.geometry.location.lng(),
        );
    

    // init autocomplete
    const initAutocomplete = () => 
        if (!searchInput.current) return;

        const autocomplete = new window.google.maps.places.Autocomplete(searchInput.current);
        autocomplete.setFields(["address_component", "geometry"]);
        autocomplete.addListener("place_changed", () => onChangeAddress(autocomplete));

    

    const reverseGeocode = ( latitude: lat, longitude: lng) => 
        const url = `$geocodeJson?key=$apiKey&latlng=$lat,$lng`;
        searchInput.current.value = "Getting your location...";
        fetch(url)
            .then(response => response.json())
            .then(location => 
              const place = location.results[0];
              const _address = extractAddress(place);
              setAddress(_address);
              searchInput.current.value = _address.plain();
              setCoords(
                lat: lat,
                lng: lng,
              );
            )
    

    const findMyLocation = () => 
        if (navigator.geolocation) 
          navigator.geolocation.getCurrentPosition(position => 
            reverseGeocode(position.coords)
          )
        
    

    // load map script after mounted
    useEffect(() => 
        initMapScript().then(() => initAutocomplete())
    , []); 

    return(
        <>        
            <form>                    
                <div>
                    <div className="search">
                        <span><Search /></span>
                        <input 
                            name="address" 
                            ref=searchInput 
                            type="text" 
                            placeholder="Search Address...." 
                        />
                        <button type="button" onClick=findMyLocation><GpsFixed /></button>
                    </div>
                </div>                      
            </form>                       
        </>
    );

我也尝试过使用 Material UI TextFieldinputRef 而不是标准的 input,但我遇到了同样的问题。此外,它在页面刷新时更频繁地发生。在第一页加载时,它几乎每次都能正常工作。

【问题讨论】:

【参考方案1】:

您是否尝试将searchInput.current 添加到useEffect 的依赖数组中?您在initAutocomplete 中有这条线if (!searchInput.current) return;,并且您的searchInput 最初设置为null。因此,在页面刷新时,在输入元素上设置的 refinitAutocomplete 函数的运行之间可能存在竞争条件。

【讨论】:

以上是关于React/Material UI - Google Places 自动完成下拉菜单有时不起作用的主要内容,如果未能解决你的问题,请参考以下文章

React Material UI - 导出多个高阶组件

响应式轮播 React material-ui

来自不同组件的 React + Material-UI 样式类在静态服务时发生冲突

React - Material-UI Accordion:如何使标题居中并展开图标

使用 React material-ui 更改 OutlinedInput 的大纲

React Material UI + 路由器重定向按钮