react-leaflet:在渲染新标记之前清除标记簇

TLDR;有没有办法使用 react-leaflet 和 react-leaflet-markercluster 从标记簇中清除所有标记?

是的,有!我刚刚向 MarkerClusterGroup 添加了一个关键道具,正如@SethLutske 推荐的那样.现在,每次重新渲染地图时,之前的标记都会被移除,只保留新的标记.

Yes, there is! I just added a key prop to the MarkerClusterGroup, as @SethLutske recommended. Now, every time there is a re-render of the map, the previous markers are removed and only the new ones remain.

<MarkerClusterGroup
    key={uuidv4()}  
    spiderfyDistanceMultiplier={1}
    showCoverageOnHover={true}
>


我在我所在州的公安机关工作.有一个 API 可以从我所在州的所需城市获取监控摄像头的集合.我正在使用 React PWA 来检索这些摄像头的图像,因此警察可以使用我的应用程序进行实时监控.警察在系统中进行身份验证,使用表格按城市过滤摄像机,结果显示在地图中,地图上带有指示每个摄像机位置的标记.触摸标记时,会显示一个弹出窗口,其中包含该摄像机的信息,以及一个按钮,触摸时会在新页面中显示摄像机图像.


I work in the Public Security agency of my state. There is an API that gets a collection of monitoring cameras from a desired city of my State. I'm working in a React PWA that retrieves the image of these cameras, so the police can use my app for live monitoring. The policemen authenticate into the system, filter the cameras by their city using a form, and the result is shown in a map with markers that indicate the position of each camera. When touching a marker, a popup is shown with information of that camera and a button that shows the camera image in a new page when touched.

地图标记和弹出窗口

我选择 Leaflet 和 react-leaflet 进行地图渲染,并选择 react-leaflet-markercluster 来对彼此相邻的标记进行分组.我有一个小问题:如果用户按城市过滤相机,然后他返回表单并决定按不同城市过滤,我想要发生的行为是:

I chose Leaflet and react-leaflet for the map rendering, and react-leaflet-markercluster to group the markers that are next each other. I'm having a little problem: if an user filters cameras by a city, then he returns to the form and decides to filter by a different city, the behavior that I wanted to happen is:

  • 清除上一个被过滤城市的所有标记;
  • 显示当前城市的所有标记;

但发生的情况是:前一个城市的标记仍保留在地图中,弹出窗口为空.

But what happens is this: the markers from the previous city remain in the map, with an empty popup.

不同城市的标记组

我的目标:我想在添加新城市的标记之前清除过去的城市标记!

这是我的 MapContainer.尽管存在我提到的错误,但它运行良好.

Here is how it is my MapContainer. It is working quite well, despite of the bug I mentioned.

      <MapContainer            
          center={[-26.627817, -51.196288]}
          zoom={6}
          scrollWheelZoom={false}
          style={{ height: '100vh', width: '100wh' }}
      >
        {//<ClearLayers /> this clears the entire map, even the tile layer, leaving it blank
        }
          <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          />
          <MarkerClusterGroup
              spiderfyDistanceMultiplier={1}
              showCoverageOnHover={false}
          >
              {cameras.map((camera: Camera) => (
                  <Marker icon={iconBTV} key={camera.name} position={[camera.latitude, camera.longitude]}>
                      <Popup>
                        {camera.name} <br /> {camera.description} <br />
                        <Link
                          to={{
                            pathname: "/cameraScreen",
                          }}
                          style={{ textDecoration: 'none' }}
                        >
                          <button
                            style={{marginTop: "1rem"}}
                            onClick={(e) => insertLog(e, camera)}
                          >Ver câmera</button>
                        </Link>                            
                      </Popup>
                  </Marker>
              ))}
          </MarkerClusterGroup>
      </MapContainer>

我尝试使用此功能,但它没有按我的意愿工作.它会破坏地图,使其完全空白.

I tried using this function, but it doesn't work as I wanted. It bugs the map, leaving it completely blank.

function ClearLayers() {
  const map = useMap();
  map.eachLayer(function (layer) {
    map.removeLayer(layer);
  });
  return null
}

我在这里搜索并接近解决方案:我能够通过使用 Leaflet 的核心 API 并留下 react-leaflet-markercluster 包装器来清除过去的城市标记 如本答案所示.在向 MCG 添加新标记之前,我确实执行了 clearLayers(),这样就可以了.这是我尝试做的:

I've searched in here and came close to a resolution: I was able to clear the past city markers by using Leaflet's core API and leaving behind react-leaflet-markercluster wrapper as shown in this answer. I do execute clearLayers() before adding new markers to the MCG, and that does the trick. Here's what I tried to do:

    import * as L from 'leaflet';
    import 'leaflet.markercluster';

    import { useMap } from 'react-leaflet';
    import { useContext, useEffect } from 'react';
    import { Link } from 'react-router-dom';
    import CameraContext from '../../contexts/CameraContext';

    const mcg = L.markerClusterGroup({
        chunkedLoading: true,
        showCoverageOnHover: false,
    });

    const MarkerCluster = ({ markers, icon }) => {
        const { setCamera } = useContext(CameraContext);

        const inserirLog = (e, camera) => {
            setCamera(camera);
            var ajax_log = new XMLHttpRequest();
            ajax_log.open('GET', `https://fakeurl.sc.gov.br/api/ajax-log-bemtevi.php?camera=${camera.nome}&servidor=${camera.servidor}`);
            ajax_log.send();
        }   

        console.log(markers);
        const map = useMap();
        useEffect(() => {
            mcg.clearLayers();
            const markerList = markers.map((camera) => {
                return L.marker(new L.LatLng(camera.latitude, camera.longitude), {
                    icon: icon,
                }).bindPopup(
                    `
                    ${camera.name} <br /> ${camera.description} <br />
                    <Link
                    to={{
                        pathname: "/cameraScreen",
                    }}
                    style={{ textDecoration: 'none' }}
                    >
                    <button
                        style={{marginTop: "1rem"}}
                        onClick={(e) => insertLog(e, camera)}
                    >Watch camera</button>
                    </Link>
                    `
                );
            });
            mcg.addLayers(markerList);
            map.fitBounds(mcg.getBounds());
            map.addLayer(mcg);
        }, [markers, icon, map]);

        return null;
    };

    export default MarkerCluster;

通过这样做,我无法在 Popup 中注入 JSX,也无法使用 react-router-dom 中的 Link 组件.我也无法将相机对象保存在上下文中以在下一页中进一步使用它.所以我结束了坚持我当前的代码,这是有效的,尽管有这个无法清除过去的城市标记".错误.

By doing that, I can't inject JSX inside the Popup, and can't use the Link component from react-router-dom. I also couldn't save the camera object in a context to use it further in the next page. So I ended sticking to my current code, that is working, despite of having this "can't clear past city markers" bug.

所以我想在使用 React 组件用于 Leaflet 地图和 Marker Cluster 时有一种方法可以清除过去的城市标记.如果 MarkerClusterGroup 有一个钩子,就像 Leaflet 地图有一个钩子一样,我想这会更容易做到:我需要做的就是这样简单的事情:

So I'd like to have a way to clear the past city markers when using the React components for Leaflet map and Marker Cluster. If there was a hook for the MarkerClusterGroup like there is one for the Leaflet map, I guess this would be easier to do: all I would need to do is something simple as this:

    const markercluster = useMarkerClusterGroup();
    markercluster.clearLayers();

但是,据我所知,react-leaflet-markercluster 中没有钩子.

But, as long as I know, there aren't hooks in react-leaflet-markercluster.

推荐答案

你可以在 MarkerClusterGroup 中添加一个 key 属性,当你希望集群成为完全重新渲染.

You can add a key prop to MarkerClusterGroup, and change it when you want the cluster to be completely rerendered.

<MarkerClusterGroup
  key={somekey}
  spiderfyDistanceMultiplier={1}
  showCoverageOnHover={false}
>

我不确定是什么逻辑触发了标记的更改,但在相同的逻辑中,当标记更改时,会生成一个新的唯一值(无论是递增的简单计数器,还是新的 uuid),将 key 设置为该新值,整个组件应卸载并重新安装.虽然如果有大量标记,这对性能来说并不是很好,但这听起来像是您正在尝试做的事情.

I'm not sure what logic is triggering the markers to change, but within that same logic, when the markers change, generate a new unique value (whether its a simple counter that increments, or a new uuid), set the key to that new value, and the entire component should unmount and remount. While this isn't great for performance if there are a huge number of markers, it sounds like what you're trying to do.

相关文章