You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Storybook集成Googleapis失败,报错google未定义求助

解决Storybook中Google Maps API加载导致google is not defined的问题

我之前在Storybook里调试依赖Google Maps的React组件时,也碰到过一模一样的问题——明明在preview-head.html里加了API脚本,组件还是报错说google未定义。这大概率是异步加载的脚本还没完成,组件就已经开始渲染执行了,或者脚本本身加载出了问题。下面是几个亲测有效的解决思路:

1. 组件内添加加载状态,等待Google对象就绪

最直接的方式是在组件里判断window.google是否存在,等它加载完成后再执行依赖代码:

import { useEffect, useState } from 'react';

const PlacesComponent = () => {
  const [isGoogleReady, setIsGoogleReady] = useState(false);

  useEffect(() => {
    // 轮询检查Google对象是否加载完成
    const checkGoogleLoaded = () => {
      if (window.google) {
        setIsGoogleReady(true);
      } else {
        // 每隔100ms检查一次,直到加载完成
        setTimeout(checkGoogleLoaded, 100);
      }
    };

    checkGoogleLoaded();

    // 清理定时器
    return () => clearTimeout(checkGoogleLoaded);
  }, []);

  // 未加载完成时显示加载态
  if (!isGoogleReady) {
    return <div>Loading Google Maps...</div>;
  }

  // 安全使用Google Maps API
  const placesService = new google.maps.places.PlacesService(document.createElement('div'));
  // ...你的业务逻辑

  return <div>Places Component Content</div>;
};

export default PlacesComponent;

2. 利用Google Maps的回调机制,精准监听加载完成

修改preview-head.html里的脚本,添加callback参数,让API加载完成后触发一个全局事件,组件监听这个事件再渲染:

第一步:更新.storybook/preview-head.html

<!-- 加入callback参数,指定加载完成后调用initGoogleMaps -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initGoogleMaps" async defer></script>

<script>
  // 定义全局回调函数,触发自定义事件
  window.initGoogleMaps = function() {
    window.dispatchEvent(new Event('googleMapsLoaded'));
  };
</script>

第二步:组件内监听自定义事件

useEffect(() => {
  const handleGoogleLoaded = () => setIsGoogleReady(true);

  // 如果脚本已经加载完成(比如刷新页面),直接设置状态
  if (window.google) {
    setIsGoogleReady(true);
  } else {
    window.addEventListener('googleMapsLoaded', handleGoogleLoaded);
  }

  // 清理事件监听
  return () => window.removeEventListener('googleMapsLoaded', handleGoogleLoaded);
}, []);

3. 检查脚本加载是否真的成功

有时候问题出在脚本本身没加载成功,比如:

  • API密钥无效或没有启用Places库权限
  • 浏览器网络请求被拦截(比如广告插件)
  • 脚本URL拼写错误

可以打开浏览器的开发者工具→网络面板,搜索maps.googleapis.com,看看请求的状态码是不是200。如果是403,那就是密钥或权限问题,去Google Cloud控制台检查配置。

4. 用Storybook装饰器统一处理加载逻辑

如果多个组件都依赖Google Maps,可以写一个Storybook装饰器,统一等待Google加载完成后再渲染组件:

import { useEffect, useState } from 'react';
import PlacesComponent from './PlacesComponent';

export default {
  title: 'Components/PlacesComponent',
  component: PlacesComponent,
  decorators: [
    (Story) => {
      const [googleReady, setGoogleReady] = useState(false);

      useEffect(() => {
        const checkGoogle = () => {
          if (window.google) {
            setGoogleReady(true);
          } else {
            const timer = setTimeout(checkGoogle, 100);
            return () => clearTimeout(timer);
          }
        };
        checkGoogle();
      }, []);

      return googleReady ? <Story /> : <div>Loading Google Maps for preview...</div>;
    },
  ],
};

export const Default = {};

5. 组件内动态加载脚本(不依赖preview-head)

如果不想依赖Storybook的preview-head.html配置,可以让组件自己动态加载Google Maps脚本:

useEffect(() => {
  const script = document.createElement('script');
  script.src = `https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places`;
  script.async = true;
  script.defer = true;
  
  // 脚本加载完成后更新状态
  script.onload = () => setIsGoogleReady(true);
  // 加载失败时提示错误
  script.onerror = () => console.error('Failed to load Google Maps API');

  document.head.appendChild(script);

  // 组件卸载时移除脚本
  return () => document.head.removeChild(script);
}, []);

这些方法里,我个人常用的是第2种(回调+自定义事件),因为它能精准控制组件渲染时机,不会有多余的轮询。你可以根据自己的场景选最合适的方案~

内容的提问来源于stack exchange,提问作者Hawaijar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:37:17