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

如何在同一NextJS页面使用GoogleMap与Autocomplete?解决API重复加载报错

解决Google Maps API重复加载的问题

问题根源是react-google-autocomplete和@react-google-maps/api各自独立加载了Google Maps API脚本,导致重复引入。解决思路是统一API加载逻辑,让两个组件复用同一套已加载的API。

具体修改步骤:

  1. 修改Map组件的API加载配置
    在useJsApiLoader中添加libraries: ["places"],因为自动补全功能依赖Google Places库,需确保加载的API包含该服务:
// Map组件修改后代码
import { GoogleCoords } from "@/typings";
import { GoogleMap, useJsApiLoader, Marker } from "@react-google-maps/api";

interface Props {
    center: GoogleCoords;
    zoom: number;
    listOfCoordinates: GoogleCoords[];
    handleOnRightClick: any;
    handleOnDragEndMarker: any;
}

const Map = ({
    center,
    zoom,
    listOfCoordinates,
    handleOnRightClick,
    handleOnDragEndMarker,
}: Props) => {
    const { isLoaded } = useJsApiLoader({
        id: "google-map-script",
        googleMapsApiKey: "API_KEY",
        // 添加places库,支持自动补全功能
        libraries: ["places"]
    });

    return (
        <>
            {isLoaded && (
                <GoogleMap
                    mapContainerStyle={{ width: 600, height: 600 }}
                    options={{
                        streetViewControl: false,
                        mapTypeId: "satellite",
                    }}
                    center={center}
                    zoom={zoom}
                    tilt={0}
                    onRightClick={handleOnRightClick}
                    clickableIcons={false}>
                    {listOfCoordinates.map((point, index) => (
                        <Marker
                            draggable={true}
                            onDragEnd={(e) => handleOnDragEndMarker(e, index)}
                            key={index}
                            position={point}
                        />
                    ))}
                </GoogleMap>
            )}
        </>
    );
};

export default Map;
  1. 禁用Autocomplete组件的自动API加载
    在页面中的Autocomplete组件添加loadScript={false}属性,让它复用Map组件已加载好的Google Maps API,不再自行加载:
// 页面组件修改后代码
import Map from "@/components/Map";
import { grayMid } from "@/theme/colors";
import { GoogleCoords } from "@/typings";
import { LegacyRef, useRef, useState } from "react";
import Autocomplete from "react-google-autocomplete";

const AddGolfCoursePage = () => {
    const [center, setCenter] = useState<GoogleCoords>({
        lat: 0,
        lng: 0,
    });
    const [zoom, setZoom] = useState(2);
    const [listOfCoordinates, setListOfCoordinates] = useState<GoogleCoords[]>(
        []
    );

    const searchRef: LegacyRef<HTMLInputElement> = useRef(null);

    const handleClickGolfCourse = (place: google.maps.places.PlaceResult) => {
        const lat: any = place.geometry?.location?.lat();
        const lng: any = place.geometry?.location?.lng();
        setCenter({ lat, lng });
        setZoom(16);
    };

    const handleClearSearchInput = () => {
        if (searchRef.current) {
            searchRef.current.value = "";
        }
    };

    const handleOnRightClick = (e: google.maps.MapMouseEvent) => {
        const coords: GoogleCoords = e.latLng?.toJSON() || { lat: 0, lng: 0 };
        setListOfCoordinates((prev) => [...prev, coords]);
    };

    const handleOnDragEndMarker = (
        e: google.maps.MapMouseEvent,
        index: number
    ) => {
        const newCoords: GoogleCoords = e.latLng?.toJSON() || {
            lat: 0,
            lng: 0,
        };
        const coordsCopy = listOfCoordinates.slice(0);
        coordsCopy.splice(index, 1, newCoords);
        setListOfCoordinates(coordsCopy);
    };

    return (
        <div style={{ padding: 8 }}>
            {/* Search Input */}
            <Autocomplete
                ref={searchRef}
                apiKey="API_KEY"
                // 禁用组件自动加载API脚本
                loadScript={false}
                style={{
                    width: "50%",
                    marginBottom: 8,
                    paddingInline: 8,
                    paddingBlock: 4,
                    borderRadius: 8,
                    zIndex: 100,
                }}
                options={{
                    types: [],
                    componentRestrictions: { country: "us" },
                }}
                onPlaceSelected={(place) => {
                    handleClickGolfCourse(place);
                }}
            />
            <button
                onClick={handleClearSearchInput}
                className="active:opacity-50"
                style={{
                    marginLeft: 8,
                    borderRadius: 8,
                    paddingInline: 8,
                    backgroundColor: grayMid,
                    paddingBlock: 4,
                }}>
                Clear
            </button>
            {/* Map */}
            <Map
                center={center}
                zoom={zoom}
                listOfCoordinates={listOfCoordinates}
                handleOnRightClick={handleOnRightClick}
                handleOnDragEndMarker={handleOnDragEndMarker}
            />
        </div>
    );
};

export default AddGolfCoursePage;

额外建议

  • 把API密钥存放在环境变量中(比如.env.local里的NEXT_PUBLIC_GOOGLE_MAPS_API_KEY),避免硬编码泄露,使用时用process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY代替字符串"API_KEY"。
  • 确保Google Cloud控制台中已经启用了Maps JavaScript API和Places API,否则功能会失效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:07:56