1

我有以下代码呈现 React Leaflet v3 地图并允许用户通过单击地图上的各个点来构建路线。总路线距离显示在地图下方。

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 => {
    // Omitted for brevity - it returns the distance between point1 and point2
  }


  const map = useMapEvents({
    click(e: any) {
      setRoute((prevValue) => {
        let lastPoint = null;
        if (prevValue.length > 0) {
            lastPoint = prevValue[prevValue.length - 1];
            const dist = calculateDistance(new LatLng(lastPoint[0], lastPoint[1]), new LatLng(e.latlng.lat, e.latlng.lng));
            console.log('marker added, calling updateDistance', dist)
            props.updateDistance(dist);
        }
        return [...prevValue, [e.latlng.lat, e.latlng.lng]];
      });
      map.panTo(e.latlng);
    }
  });

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

function App() {

  const [distance, setDistance] = useState(0);
  const updateDistance = (d: number) => {
      console.log('update distance called', d)
      setDistance((prevValue) => { 
        console.log('setting new distance', d);
        return  prevValue + d
      });
  };

  return (
    <React.Fragment>
      <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>
     </React.Fragment>
  );
}

可以在这里找到一个 StackBlitz 。请注意,标记无法在地图上正确呈现,但这对于演示目的而言无关紧要。

使用这种方法,我distance在顶级组件中使用useState并传递一个处理程序来更新组件的总距离Route

const updateDistance = (d: number) => {
  console.log('update distance called', d)
  setDistance((prevValue) => { 
    console.log('setting new distance', d);
    return  prevValue + d
  });
};

<MapContainer ...>
  ...
  <Route updateDistance={updateDistance} />
</MapContainer>

其中定义的点击处理程序useMapEvents更新route状态(存在于Route组件中),并且还调用updateDistance通过传递的函数props

const map = useMapEvents({
  click(e: any) {
    setRoute((prevValue) => {
      let lastPoint = null;
      if (prevValue.length > 0) {
        lastPoint = prevValue[prevValue.length - 1];
        const dist = calculateDistance(new LatLng(lastPoint[0], lastPoint[1]), new LatLng(e.latlng.lat, e.latlng.lng));
        console.log('marker added, calling updateDistance', dist)
        props.updateDistance(dist);
      }
      return [...prevValue, [e.latlng.lat, e.latlng.lng]];
    });
    map.panTo(e.latlng);
  }
});

当点被添加到路线时,我可以看到updateDistance(因此setDistance)被调用了两次。在上面的 StackBlitz 中打开控制台查看输出。

route我可以通过在顶级组件中定义状态并将其传递给组件来“解决”该问题,RouteStackBlitz 所示。虽然这可行,但我在组件route内定义状态和顶级组件中的状态背后的基本原理是,顶级组件只需要知道距离而不是实际路线的细节。Routedistance

我试图理解为什么第一个 StackBlitz 的行为如此。我不相信我在任何地方都在改变状态,并且总是为 theroutedistance状态返回一个新值,而且我还在使用setState状态更新需要使用前一个状态的值的功能版本。

4

0 回答 0