google-map-react中useRef调用fitBounds报非函数错误
google-map-react 实现标记点自动适配边界方案
报错原因
通过useRef绑定组件拿到的mapRef.current是google-map-react封装的React组件实例,并非Google Maps JS API提供的原生地图对象,这个封装实例没有挂载fitBounds方法,直接调用就会抛出TypeError: mapRef.current.fitBounds is not a function错误。
可行实现方案
方案1:使用组件内置bounds属性(最简便)
GoogleMapReact组件原生支持bounds属性,只需要计算出所有标记点的边界坐标,按格式传入这个属性,组件会自动完成中心点计算、缩放级别调整,完全不需要手动调用API:
- 遍历所有标记点,找出纬度最大值、纬度最小值、经度最大值、经度最小值
- 组装成bounds对象:
{ ne: { lat: 最大纬度, lng: 最大经度 }, sw: { lat: 最小纬度, lng: 最小经度 } } - 把这个对象传给
GoogleMapReact的bounds属性即可,不需要额外绑定ref、写手动调用逻辑
方案2:拿到原生地图实例调用原生fitBounds
如果需要手动触发适配(比如点击按钮才适配、标记点动态加载后触发),可以通过组件提供的onGoogleApiLoaded回调拿到原生地图实例,再调用原生API的fitBounds方法:
- 新增ref存储原生地图实例,不要直接把ref绑在组件上拿包装实例
- 给
GoogleMapReact传入onGoogleApiLoaded回调,回调触发时把参数里的原生map对象存到ref中 - 需要适配时,遍历所有标记点生成
LatLngBounds对象,调用原生map的fitBounds方法即可
修正后的可运行代码示例
import GoogleMapReact from "google-map-react"; import { Place } from "@material-ui/icons"; import { useRef, useMemo, useState } from "react"; const Page = () => { // 存储原生Google地图实例,不是React组件实例 const nativeMapRef = useRef(null); // 示例标记点数据,替换成实际业务数据源即可 const [currentSearchData, setCurrentSearchData] = useState([ { latitude: 38.103, longitude: -121.572, name: "点位1" }, { latitude: 38.12, longitude: -121.55, name: "点位2" }, { latitude: 38.09, longitude: -121.59, name: "点位3" }, ]); const [view, setView] = useState("recherche"); const defaultCenter = { lat: 38.103, lng: -121.572 }; const MarkerPlace = (props) => { return ( <div> <Place fontSize={"large"}></Place> <p>{props.name}</p> </div> ) } // 自动计算bounds(方案1直接传这个值给组件的bounds属性即可) const mapBounds = useMemo(() => { if (!currentSearchData?.length) return null; let minLat = Infinity, maxLat = -Infinity, minLng = Infinity, maxLng = -Infinity; currentSearchData.forEach(item => { minLat = Math.min(minLat, item.latitude); maxLat = Math.max(maxLat, item.latitude); minLng = Math.min(minLng, item.longitude); maxLng = Math.max(maxLng, item.longitude); }); // 预留少量边距,避免标记点贴到地图边缘被控件遮挡 const latPadding = (maxLat - minLat) * 0.1 || 0.01; const lngPadding = (maxLng - minLng) * 0.1 || 0.01; return { ne: { lat: maxLat + latPadding, lng: maxLng + lngPadding }, sw: { lat: minLat - latPadding, lng: minLng - lngPadding } } }, [currentSearchData]); // 手动触发fitBounds(方案2逻辑) const handleFitBounds = () => { if (!nativeMapRef.current || !window.google?.maps) return; const bounds = new window.google.maps.LatLngBounds(); currentSearchData.forEach(item => { bounds.extend(new window.google.maps.LatLng(item.latitude, item.longitude)); }); nativeMapRef.current.fitBounds(bounds); } return ( <div style={{ height: "100vh", width: "100%" }}> <GoogleMapReact bootstrapURLKeys={{ key: "替换为实际申请的地图Key" }} defaultCenter={defaultCenter} defaultZoom={13} // 方案1:直接传bounds属性,加载即自动适配 bounds={mapBounds} // 回调中拿到原生地图实例存储 onGoogleApiLoaded={({ map }) => { nativeMapRef.current = map; }} options={{ fullscreenControl: false }} > {view === "recherche" && currentSearchData.map((activity, index) => ( <MarkerPlace key={index} lat={activity.latitude} lng={activity.longitude} name={activity.name} /> ))} </GoogleMapReact> <button onClick={handleFitBounds} style={{ position: "absolute", top: 20, left: 20, zIndex: 10 }}> 适配所有点位 </button> </div> ) } export default Page;
注意事项
- 不要把ref直接挂在
GoogleMapReact组件上调用原生API,拿到的封装实例只暴露了极少部分内部方法,没有原生地图的完整能力 - 计算bounds的时候建议加少量边距,避免最边缘的标记点被地图缩放控件、版权信息遮挡
- 如果标记点是异步加载的,等数据加载完成后再更新bounds或者触发fitBounds即可,不需要手动编写复杂的缩放级别计算逻辑
内容的提问来源于stack exchange,提问作者Lucas Bodin
相关产品推荐
相关产品推荐

