如何避免Google Map组件Marker重渲染?仅更新标记位置
解决方案:仅更新Marker位置避免Map组件重渲染
1. 直接通过Marker实例更新位置(最有效)
利用useAdvancedMarkerRef获取Marker的原生实例,当设备位置更新时,直接调用实例的position属性修改位置,无需触发组件重渲染。
在你的CutomMap组件中添加以下useEffect:
useEffect(() => { if (!marker) return; // 计算新位置 const newPosition = getMarkerPosition(device, simulatedPosition); // 直接更新Marker实例的位置 marker.position = newPosition; }, [device, simulatedPosition, marker]);
这样即使CutomMap组件因其他原因重渲染,Marker的位置更新不会触发整个Map的重渲染,而是直接操作Google Maps的原生API修改位置。
2. 用React.memo优化Marker组件
将主设备Marker抽离为独立组件,并使用React.memo包裹,只有当Marker的核心props变化时才重渲染:
创建DeviceMarker.jsx:
import React, { memo } from 'react'; import { AdvancedMarker, useAdvancedMarkerRef, Pin } from '@vis.gl/react-google-maps'; import { getMarkerLabel, getMarkerPosition } from "../utils/mapUtils.js"; const DeviceMarker = memo(({ device, simulatedPosition, onClick }) => { const markerPosition = getMarkerPosition(device, simulatedPosition); const label = device?.device?.bike?.name; const markerLabel = getMarkerLabel(label); const [markerRef] = useAdvancedMarkerRef(); return ( <AdvancedMarker position={markerPosition} ref={markerRef} title={markerLabel} onClick={onClick} > <Pin background={'#4611a7'} borderColor={'white'} glyphColor={'white'}> {markerLabel} </Pin> </AdvancedMarker> ); }); export default DeviceMarker;
然后在CutomMap中替换原Marker代码:
{shouldShowMarker && ( <DeviceMarker device={device} simulatedPosition={simulatedPosition} onClick={() => setMarkerInfo(true)} /> )}
3. 优化CutomMap组件的状态与重渲染
- 将
center和zoom从useState改为useMemo,确保设备位置更新时能正确更新,同时避免不必要的状态更新:
import { useMemo } from 'react'; // 替换原有的useState定义 const zoom = useMemo(() => device?.lastLocation ? DEFAULT_ZOOM_IN : DEFAULT_ZOOM_OUT, [device]); const center = useMemo(() => device?.lastLocation ? { lat: device.lastLocation.lat, lng: device.lastLocation.lon } : DEFAULT_CENTER, [device]);
- 用
React.memo包裹CutomMap组件,避免父组件传递不变的props时触发重渲染:
import { memo } from 'react'; // 替换原有的export语句 export default memo(CutomMap);
4. 优化附近设备Marker的渲染
将附近设备的Marker也抽离为独立组件并使用React.memo,避免nearbyDevices更新时所有Marker都重渲染:
const NearbyDeviceMarker = memo(({ item, mainIMSI }) => { const loc = item.device?.location; const label = item.device?.bikeName; const markerLabel = getMarkerLabel(label); const itemId = item.device?.imsi; if (!loc?.lat || !loc?.lon || mainIMSI === itemId) return null; return ( <AdvancedMarker key={`nearby-${item.device.imsi}`} position={{ lat: loc.lat, lng: loc.lon }} title={markerLabel} > <Pin background={'#4611a7'} borderColor={'white'} glyphColor={'black'}> {markerLabel} </Pin> </AdvancedMarker> ); });
然后在CutomMap中替换原代码:
{SIMULATION_MODE && nearbyDevices.map((item) => ( <NearbyDeviceMarker key={`nearby-${item.device.imsi}`} item={item} mainIMSI={device?.device?.imsi} /> ))}
关键原因说明
你之前抽离Marker组件未生效,大概率是因为:
- 未使用
React.memo包裹组件,导致父组件重渲染时Marker也跟着重渲染 - 原组件中
center和zoom用useState定义,初始值仅执行一次,设备位置更新时无法正确更新,间接导致Map组件异常重渲染
内容的提问来源于stack exchange,提问作者Hedara Alhasan
相关产品推荐
相关产品推荐

