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

React Leaflet v3 点击地图构建路线时setState触发两次问题排查

问题原因

  • 核心问题是你将带有副作用的props.updateDistance(dist)调用放在了setRoute的状态更新回调函数中。
    React官方要求传入setState的更新函数必须是纯函数,不能包含任何副作用(包括调用外部方法、修改外部变量、打印日志等操作)。在React 18及以上版本的严格模式下,React会主动执行两次状态更新函数,用于排查开发者编写的非纯函数逻辑,这就是你看到updateDistance被调用两次的直接原因。
  • 次要原因是你在App组件中定义的updateDistance函数没有做缓存,每次App组件重渲染时都会生成新的函数实例,导致Route组件接收的props变化触发重渲染,也可能额外触发事件绑定的重复执行。

解决方案

你不需要把route状态移到父组件,只要调整副作用的执行位置即可,完全保留Route组件内部维护route状态的设计:

  1. 把距离计算和updateDistance调用从setRoute的更新回调中移到useEffect中,监听route状态变化后再执行对应逻辑
  2. 用useCallback缓存App组件中的updateDistance函数,避免不必要的重渲染

修改后的核心代码示例

Route组件调整

interface RouteProps {
  updateDistance: (d: number) => void;
}

function Route(props: RouteProps) {
  const initialRoute: [number, number][] = [];
  const [route, setRoute] = useState(initialRoute);

  const calculateDistance = (point1: LatLng, point2: LatLng): number => {
    // 原有距离计算逻辑不变
  }

  const map = useMapEvents({
    click(e: any) {
      // 状态更新回调仅做纯逻辑,返回新的状态值
      setRoute((prevValue) => [...prevValue, [e.latlng.lat, e.latlng.lng]]);
      map.panTo(e.latlng);
    }
  });

  // 用useEffect处理副作用:状态变化后计算距离并通知父组件
  useEffect(() => {
    if (route.length < 2) return;
    const lastPointIndex = route.length - 1;
    const prevPoint = route[lastPointIndex - 1];
    const currentPoint = route[lastPointIndex];
    const dist = calculateDistance(
      new LatLng(prevPoint[0], prevPoint[1]),
      new LatLng(currentPoint[0], currentPoint[1])
    );
    props.updateDistance(dist);
  }, [route, props.updateDistance]);

  return (
    <>
      {route.map((point, idx) => <Marker key={idx} position={new LatLng(point[0], point[1])} />)}
      <Polyline pathOptions={{fillColor: 'blue'}} positions={route} />
    </>
  );
}

App组件调整

function App() {
  const [distance, setDistance] = useState(0);
  // 用useCallback缓存更新函数,避免不必要的重渲染
  const updateDistance = useCallback((d: number) => {
    setDistance((prevValue) => prevValue + d);
  }, []);

  return (
    <>
      <MapContainer
        center={[55, -2]}
        zoom={12}
        scrollWheelZoom={false}
        style={{ width: "100vw", height: "80vh" }}
      >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <Route updateDistance={updateDistance} />
      </MapContainer>
      <h6>Distance: {distance.toFixed(2)} miles</h6>
    </>
  );
}

内容的提问来源于stack exchange,提问作者Ian A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 08:54:10