React Google Maps拖拽地图闪烁重置问题求助
拖拽Google地图时闪烁并重置到初始状态的问题解决
核心问题分析
你的问题根源有两个关键点:
- 硬编码的地图初始属性:
GoogleMap组件的zoom={4}和center={DEFAULT_CENTER}是固定值,每当refetch触发组件重渲染时,地图会强制重置回这个初始缩放和中心点,直接导致拖拽时闪烁回原位。 - 过于频繁的事件触发:
onBoundsChanged事件在拖拽过程中会被连续触发(每帧都可能触发),每次触发都调用refetch,导致组件反复重渲染,放大了地图重置的问题——哪怕换成drag_end,只要地图属性还是硬编码,重渲染时依然会重置。
解决方案
步骤1:用状态管理地图的实时属性
把固定的zoom和center改成React状态,让组件重渲染时使用当前地图的状态,而非初始值:
const [zoom, setZoom] = useState(4); const [center, setCenter] = useState(DEFAULT_CENTER);
步骤2:替换事件监听,减少不必要的refetch
放弃onBoundsChanged,改用onDragEnd和onZoomChanged事件,只在拖拽结束或缩放完成后触发数据更新,避免频繁触发:
const handleMapChange = useCallback(() => { const bounds = map?.getBounds()?.toJSON(); if (bounds) { refetch({ bounds }); } // 更新当前地图的状态 if (map) { setZoom(map.getZoom() || 4); setCenter(map.getCenter()?.toJSON() || DEFAULT_CENTER); } }, [map, refetch]);
步骤3:优化Apollo Query的触发逻辑
原来的useQuery直接依赖map?.getBounds()作为变量,会导致组件每次渲染都重新计算值,触发不必要的查询。改成初始变量为空,只在地图加载完成和交互结束时触发查询:
const { data = { map: [] }, loading, error, refetch, } = useQuery(GET_PROPERTIES_ON_MAP, { variables: { bounds: null }, skip: !map, // 地图未加载完成前不执行查询 });
同时在onLoad中触发初始数据查询:
const onLoad = useCallback( (mapInstance: google.maps.Map) => { mapInstance.fitBounds(EUROPE_BOUNDS); setMap(mapInstance); // 初始化地图状态 setZoom(mapInstance.getZoom() || 4); setCenter(mapInstance.getCenter()?.toJSON() || DEFAULT_CENTER); // 加载初始数据 const initialBounds = mapInstance.getBounds()?.toJSON(); if (initialBounds) { refetch({ bounds: initialBounds }); } }, [setMap, refetch] );
完整修改后的代码
export const Map = () => { const [map, setMap] = useState<google.maps.Map | null>(null); const [zoom, setZoom] = useState(4); const [center, setCenter] = useState(DEFAULT_CENTER); const { data = { map: [] }, loading, error, refetch, } = useQuery(GET_PROPERTIES_ON_MAP, { variables: { bounds: null }, skip: !map, }); const onLoad = useCallback( (mapInstance: google.maps.Map) => { mapInstance.fitBounds(EUROPE_BOUNDS); setMap(mapInstance); setZoom(mapInstance.getZoom() || 4); setCenter(mapInstance.getCenter()?.toJSON() || DEFAULT_CENTER); const initialBounds = mapInstance.getBounds()?.toJSON(); if (initialBounds) { refetch({ bounds: initialBounds }); } }, [setMap, refetch] ); const options: GoogleMapProps["options"] = { restriction: { latLngBounds: EUROPE_BOUNDS, strictBounds: false, }, }; const handleMapChange = useCallback(() => { const bounds = map?.getBounds()?.toJSON(); if (bounds) { refetch({ bounds }); } if (map) { setZoom(map.getZoom() || 4); setCenter(map.getCenter()?.toJSON() || DEFAULT_CENTER); } }, [map, refetch]); if (loading) { return <p>Loading ...</p>; } return ( <div style={{ height: 400, borderRadius: 8, overflow: "hidden", marginTop: 32, }} > <GoogleMap mapContainerStyle={{ height: "100%" }} zoom={zoom} center={center} onLoad={onLoad} onDragEnd={handleMapChange} onZoomChanged={handleMapChange} clickableIcons options={options} > {data.map.map((property: Property) => ( <Marker key={property.id} lat={property.lat} lng={property.lng} /> ))} </GoogleMap> </div> ); };
内容的提问来源于stack exchange,提问作者Kaiser Soze
相关产品推荐
相关产品推荐

