React中状态变更时如何阻止Google Map重载?
问题解决:Google Map组件重载与视图重置问题
问题场景
React应用中集成Google Map组件,用户勾选复选框后对应标记/路线会显示在地图上。当前存在两个问题:
- 初始地图中心为非洲、缩放级别3;用户放大视图后取消勾选复选框,地图会重载并重置回初始位置。
- 控制台出现警告:
253 useJsApiLoader.tsx:62 Performance warning! LoadScript has been reloaded unintentionally!
相关代码片段:
Map组件代码
<GoogleMap center={center} zoom={3} onLoad={(map) => setMap(map)} mapContainerStyle={{ width: '100%', height: '100%' }} > {data.toDisplayOnMap.length > 0 && ( <> {data.toDisplayOnMap.map((element, i) => { if ('events' in element.item) { return <Marker key={i} position={element.item.locationCoords} onClick={() => navigate(`/location?id=${element.id}`)} icon={`http://labs.google.com/ridefinder/images/mm_20_red.png`} /> } else if ('waypoints' in element.item) { if (element.item.traveltype === 'location') { return <DirectionsRenderer options={{ preserveViewport: true, polylineOptions: { strokeColor: 'blue', strokeWeight: 2 }, markerOptions: { icon: `http://labs.google.com/ridefinder/images/mm_20_yellow.png` } }} directions={element.route} /> } else { let wps = element.item.waypoints;; let coords = []; wps.forEach((wp) => { wp.coords.forEach((coord) => { coords.push(coord) }) }) return ( <> <Marker position={coords[0]} icon={`http://labs.google.com/ridefinder/images/mm_20_yellow.png`} /> <Marker position={coords[coords.length - 1]} icon={`http://labs.google.com/ridefinder/images/mm_20_yellow.png`} /> <Polyline path={coords} options={{ geodesic: true, strokeColor: '#000', strokeOpacity: 1, strokeWeight: 2, editable: false, draggable: false, clickable: true }} /> </> ) } } })} </> )} </GoogleMap>
地图实例与API加载代码
const [map, setMap] = useState(/** @type google.maps.Map */(null)) const { isLoaded } = useJsApiLoader({ googleMapsApiKey: ct.MAPS_API_KEY, libraries: p })
解决方案
1. 阻止地图视图重置到初始位置
问题根源:GoogleMap组件的center和zoom props使用固定初始值,组件重新渲染时会强制重置视图。改为受控模式,用状态保存用户调整后的视图参数:
- 第一步:添加状态保存当前视图
import { useState, useRef } from 'react'; // 初始视图配置 const initialCenter = { /* 你的初始非洲中心坐标 */ }; const [mapView, setMapView] = useState({ center: initialCenter, zoom: 3 }); // 用useRef存地图实例比useState更稳定 const mapRef = useRef(null);
- 第二步:修改
GoogleMap组件props,监听视图变化更新状态
<GoogleMap center={mapView.center} zoom={mapView.zoom} onLoad={(mapInstance) => { mapRef.current = mapInstance; }} // 监听中心变化,更新状态 onCenterChanged={() => { if (mapRef.current) { setMapView(prev => ({ ...prev, center: mapRef.current.getCenter() })); } }} // 监听缩放变化,更新状态 onZoomChanged={() => { if (mapRef.current) { setMapView(prev => ({ ...prev, zoom: mapRef.current.getZoom() })); } }} mapContainerStyle={{ width: '100%', height: '100%' }} > {/* 子元素保持原逻辑,后面优化 */} </GoogleMap>
2. 解决LoadScript重复加载警告
问题根源:useJsApiLoader的libraries参数每次渲染生成新数组,导致hook认为依赖变化,重新加载脚本。用useMemo缓存稳定的依赖:
import { useMemo } from 'react'; // 缓存libraries数组,确保引用稳定 const cachedLibraries = useMemo(() => p, []); // 若p依赖其他变量,需添加到依赖数组中 const { isLoaded } = useJsApiLoader({ googleMapsApiKey: ct.MAPS_API_KEY, libraries: cachedLibraries });
3. 避免地图子元素不必要重渲染
问题根源:data.toDisplayOnMap每次渲染可能生成新数组,导致地图子元素频繁重渲染,甚至触发地图组件重载。用useMemo缓存地图元素:
const mapElements = useMemo(() => { if (data.toDisplayOnMap.length === 0) return null; return data.toDisplayOnMap.map((element, i) => { if ('events' in element.item) { return <Marker key={i} position={element.item.locationCoords} onClick={() => navigate(`/location?id=${element.id}`)} icon={`http://labs.google.com/ridefinder/images/mm_20_red.png`} />; } else if ('waypoints' in element.item) { if (element.item.traveltype === 'location') { return <DirectionsRenderer options={{ preserveViewport: true, polylineOptions: { strokeColor: 'blue', strokeWeight: 2 }, markerOptions: { icon: `http://labs.google.com/ridefinder/images/mm_20_yellow.png` } }} directions={element.route} />; } else { const coords = []; element.item.waypoints.forEach(wp => { wp.coords.forEach(coord => coords.push(coord)); }); return ( <> <Marker position={coords[0]} icon={`http://labs.google.com/ridefinder/images/mm_20_yellow.png`} /> <Marker position={coords[coords.length - 1]} icon={`http://labs.google.com/ridefinder/images/mm_20_yellow.png`} /> <Polyline path={coords} options={{ geodesic: true, strokeColor: '#000', strokeOpacity: 1, strokeWeight: 2, editable: false, draggable: false, clickable: true }} /> </> ); } } return null; }); }, [data.toDisplayOnMap, navigate]); // 添加所有依赖变量到数组中
然后在GoogleMap中替换子元素:
<GoogleMap // ...其他props > {mapElements} </GoogleMap>
核心逻辑说明
- 视图状态由用户操作决定,不再强制重置到初始值
- 稳定所有hook依赖项,避免不必要的脚本重载和组件重渲染
- 缓存地图元素,减少DOM操作开销
内容的提问来源于stack exchange,提问作者Calin Onaca
相关产品推荐
相关产品推荐

