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

React Google Maps拖拽地图闪烁重置问题求助

拖拽Google地图时闪烁并重置到初始状态的问题解决

核心问题分析

你的问题根源有两个关键点:

  1. 硬编码的地图初始属性:GoogleMap组件的zoom={4}和center={DEFAULT_CENTER}是固定值,每当refetch触发组件重渲染时,地图会强制重置回这个初始缩放和中心点,直接导致拖拽时闪烁回原位。
  2. 过于频繁的事件触发: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 20:42:22