React集成Google Maps API时出现'Uncaught (in promise) TypeError'错误求助
React中Google Maps自动补全组件报错分析与解决
问题现象
使用Google Maps JavaScript API实现地点自动补全组件时,持续触发以下错误:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'zt')
组件实现代码
import React, { useRef, useEffect, useState } from "react"; function GoogleMapsAutocomplete({ onPlaceSelect }) { const autocompleteInput = useRef(null); const [autocomplete, setAutocomplete] = useState(null); useEffect(() => { const loadGoogleMapsScript = () => { // 检查Google Maps脚本是否已加载 if (!window.google) { const script = document.createElement("script"); script.src = `https://maps.googleapis.com/maps/api/js?key=MyAPIKey&libraries=places`; script.async = true; script.defer = true; // 通过回调确保初始化完成 script.onload = () => { initAutocomplete(); }; document.head.appendChild(script); } else { initAutocomplete(); } }; const initAutocomplete = () => { const auto = new window.google.maps.places.Autocomplete( autocompleteInput.current ); auto.addListener("place_changed", handlePlaceSelect); setAutocomplete(auto); // 初始化完成后保存autocomplete对象 }; const handlePlaceSelect = () => { if (autocomplete) { // 检查autocomplete是否已定义 const selectedPlace = autocomplete.getPlace(); if (selectedPlace) { onPlaceSelect(selectedPlace); } } }; loadGoogleMapsScript(); }, [onPlaceSelect]); return ( <div> <input ref={autocompleteInput} type="text" placeholder="Enter a location" /> </div> ); } export default GoogleMapsAutocomplete;
错误原因分析
- 状态异步更新导致的实例获取延迟:
handlePlaceSelect依赖组件状态autocomplete,但setAutocomplete是异步操作,当place_changed事件触发时,状态可能还未完成更新,此时autocomplete为null,调用其方法会引发API内部错误。 - DOM元素未完全挂载:组件挂载时
autocompleteInput.current可能还未插入DOM树,直接传入Google Maps API会导致初始化异常,触发内部属性未定义的报错。 - 重复初始化风险:若组件多次渲染,可能重复创建Autocomplete实例,引发API内部状态冲突。
解决方法
方案1:直接使用局部实例处理事件
避免依赖组件状态,在初始化时直接用创建的auto实例绑定事件:
const initAutocomplete = () => { if (!autocompleteInput.current) return; // 确保DOM元素存在 const auto = new window.google.maps.places.Autocomplete( autocompleteInput.current ); // 直接使用局部实例处理事件,跳过组件状态依赖 auto.addListener("place_changed", () => { const selectedPlace = auto.getPlace(); if (selectedPlace) { onPlaceSelect(selectedPlace); } }); setAutocomplete(auto); };
方案2:确保DOM完全挂载后初始化
使用useLayoutEffect替代useEffect,确保DOM更新完成后再执行API初始化:
useLayoutEffect(() => { const loadGoogleMapsScript = () => { // 原有脚本加载逻辑不变 if (!window.google) { const script = document.createElement("script"); script.src = `https://maps.googleapis.com/maps/api/js?key=MyAPIKey&libraries=places`; script.async = true; script.defer = true; script.onload = () => initAutocomplete(); document.head.appendChild(script); } else { initAutocomplete(); } }; const initAutocomplete = () => { if (!autocompleteInput.current) return; const auto = new window.google.maps.places.Autocomplete( autocompleteInput.current ); auto.addListener("place_changed", () => { const selectedPlace = auto.getPlace(); if (selectedPlace) { onPlaceSelect(selectedPlace); } }); setAutocomplete(auto); }; loadGoogleMapsScript(); }, [onPlaceSelect]);
方案3:添加初始化标记防止重复创建实例
使用useRef标记是否已完成初始化,避免重复创建Autocomplete实例:
const autocompleteInput = useRef(null); const [autocomplete, setAutocomplete] = useState(null); const isInitialized = useRef(false); // 初始化状态标记 useEffect(() => { if (isInitialized.current) return; const loadGoogleMapsScript = () => { if (!window.google) { const script = document.createElement("script"); script.src = `https://maps.googleapis.com/maps/api/js?key=MyAPIKey&libraries=places`; script.async = true; script.defer = true; script.onload = () => initAutocomplete(); document.head.appendChild(script); } else { initAutocomplete(); } }; const initAutocomplete = () => { if (!autocompleteInput.current) return; const auto = new window.google.maps.places.Autocomplete( autocompleteInput.current ); auto.addListener("place_changed", () => { const selectedPlace = auto.getPlace(); if (selectedPlace) { onPlaceSelect(selectedPlace); } }); setAutocomplete(auto); isInitialized.current = true; // 标记已完成初始化 }; loadGoogleMapsScript(); // 组件卸载时清理监听 return () => { if (autocomplete) { window.google.maps.event.clearInstanceListeners(autocomplete); } isInitialized.current = false; }; }, [onPlaceSelect]);
额外注意事项
- 确认你的API密钥已启用Places API权限,且未超出调用配额或被限制访问。
- 若为SPA应用,建议将Google Maps脚本加载放在全局入口文件中,避免组件重复处理脚本加载逻辑。
内容的提问来源于stack exchange,提问作者Leonardo Picchi Lpdancona
相关产品推荐
相关产品推荐

