React中Google未定义问题:如何检测Google Maps脚本是否加载完成
我之前也碰到过一模一样的问题——网络不给力的时候,Google Maps脚本加载慢半拍,直接导致渲染时google对象未定义的报错。不用react-async-script-loader的话,有几个不用大改代码的方案可以试试:
方案一:监听脚本加载事件,用状态控制渲染时机
这是最直接的方法,给你动态创建的脚本添加onload和onerror事件,只有当脚本加载完成后才渲染包含地图的组件。修改你的代码如下:
constructor(props) { super(props); this.state = { isMapLoaded: false, mapLoadFailed: false }; } componentWillMount() { const API_KEY = process.env.GOOGLEMAP_API_KEY; const script = document.createElement('script'); script.src = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}`; script.async = true; script.defer = true; // 脚本加载成功,标记状态 script.onload = () => { this.setState({ isMapLoaded: true }); }; // 脚本加载失败(比如API密钥错误、网络完全中断) script.onerror = () => { this.setState({ mapLoadFailed: true }); }; document.head.append(script); } renderApp() { return ( <div> <Header /> <Main /> </div> ); } render() { // 加载失败的提示 if (this.state.mapLoadFailed) { return <div>地图加载失败,请检查网络或稍后重试</div>; } // 未加载完成时显示加载状态 if (!this.state.isMapLoaded) { return <div>正在加载地图...</div>; // 可以换成你的加载动画组件 } return <div>{this.renderApp()}</div>; }
这个方案的核心是通过脚本的onload事件确认Google Maps API已完全加载,再通过组件状态控制渲染,从根源上避免google is undefined的问题。
方案二:轮询检查google对象,兼容复杂加载场景
如果你的脚本可能在多个地方被加载,或者想兼容脚本已提前加载的情况,可以用轮询的方式定期检查google对象是否存在:
constructor(props) { super(props); this.state = { isMapLoaded: false, mapLoadTimeout: false }; this.checkInterval = null; } componentDidMount() { // 先检查是否已经加载完成 if (typeof google !== 'undefined' && google.maps) { this.setState({ isMapLoaded: true }); return; } // 启动轮询,每隔300ms检查一次,超时时间设为10秒 const timeoutLimit = 10000; const startTime = Date.now(); this.checkInterval = setInterval(() => { if (typeof google !== 'undefined' && google.maps) { clearInterval(this.checkInterval); this.setState({ isMapLoaded: true }); } else if (Date.now() - startTime > timeoutLimit) { clearInterval(this.checkInterval); this.setState({ mapLoadTimeout: true }); } }, 300); // 确保脚本被加载(避免重复加载) const API_KEY = process.env.GOOGLEMAP_API_KEY; const existingScript = document.querySelector(`script[src="https://maps.googleapis.com/maps/api/js?key=${API_KEY}"]`); if (!existingScript) { const script = document.createElement('script'); script.src = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}`; script.async = true; script.defer = true; document.head.append(script); } } componentWillUnmount() { // 清理定时器,防止内存泄漏 if (this.checkInterval) { clearInterval(this.checkInterval); } } render() { if (this.state.mapLoadTimeout) { return <div>地图加载超时,请检查网络连接</div>; } if (!this.state.isMapLoaded) { return <div>正在加载地图...</div>; } return <div>{this.renderApp()}</div>; }
这个方案适合脚本加载时机不确定的场景,轮询能确保等到google对象可用后再渲染组件。记得在组件卸载时清理定时器,避免内存泄漏。
方案三:通过HTML脚本的callback参数触发渲染
把脚本加载放到index.html中,利用Google Maps API的callback参数指定一个全局函数,当脚本加载完成时触发这个函数,通知React应用开始渲染:
首先在index.html中添加脚本:
<script src="https://maps.googleapis.com/maps/api/js?key=%REACT_APP_GOOGLEMAP_API_KEY%&callback=onMapLoaded" async defer></script>
然后修改你的App组件:
constructor(props) { super(props); this.state = { isMapLoaded: false }; // 定义全局回调函数 window.onMapLoaded = () => { this.setState({ isMapLoaded: true }); }; } render() { if (!this.state.isMapLoaded) { return <div>加载中...</div>; } return <div>{this.renderApp()}</div>; }
这种方法把脚本加载交给HTML处理,通过官方的回调机制确保加载完成后再渲染,缺点是要注意全局函数的命名冲突问题。
个人最推荐方案一,因为它和你现有的代码结构最契合,改动最小,逻辑也最清晰,能完美解决网络慢导致的加载时序问题。
内容的提问来源于stack exchange,提问作者bigmugcup
相关产品推荐
相关产品推荐

