如何在地图加载时打开特定的弹出窗口?

How to open a specific popup on map load?

在 Express 服务器上使用 React-Leaflet V3.1.0。

我的用户请求能够使用以下格式的 link:

https://www.mymap.com/id/:category/:id/:zoom

这会向服务器发送一个请求,以获取他们 link 前往的标记的 lat/lon 坐标,然后将开始位置设置为这些坐标。我已经设法使用 React Router Dom 使这部分工作。现在理想情况下,我希望能够在地图加载时打开这个特定标记的弹出窗口。通过我设置查询组件的方式,在生成标记以指示这是感兴趣的标记时能够通过道具传递布尔值不是问题,我只是不确定如何激活弹出窗口。

我不是很熟悉 useRef 以及如何准确地使用它来访问不同的 Leaflet 方法,但是我昨天做了一些实验并且能够得到一些似乎是朝着正确方向迈出的一步的行为。弹出窗口在初始启动时没有加载,但当我在地图上导航时,其他弹出窗口开始自动打开。话虽这么说,我的地图渲染了数千个标记并且每个标记超过 1 个渲染不值得权衡这个相对小众的功能,除非可以将第二个渲染隔离到仅感兴趣的标记。

我认为可以将我带到我想要的地方的方法,但我无法触发这个特定的方法: https://leafletjs.com/reference-1.6.0.html#popup-openon

我最终使用了类似于 link 的 openPopup()https://stackblitz.com/edit/react-awtgn6?file=Map.js

但我只能在某种程度上(上面提到的结果)使用 useEffect + useRef。

在状态中存储弹出窗口打开状态时,我觉得我遗漏了一些明显的东西?

基本代码示例:

<Marker
  position={[item.lat, item.lon]}
  icon={getIcon(item)}
>
  <Popup position={[item.lat, item.lon]}>
    <PopupContent
      item={item}
      userSettings={userSettings}
    />
  </Popup>
</Marker>

谢谢!

如果目标是打开标记的弹出窗口,条件是标记的 id 在 url 参数中指定(即 url?id=3),这是可行的.

一个 ref 对象中的多个 ref

您需要映射一组标记,每个标记都有一个唯一的 ID。您需要保留 ref 的 数组或对象 ,并且在映射时,在 ref 属性中,将 ref 添加到该对象数组:

const Map = (props) => {
  const [done, setDone] = React.useState(false);
  const markerRefs = React.useRef({});

return (
    <MapContainer {...mapContainerProps}>
      {data.map((marker, index) => {
        return (
          <Marker
            position={marker.coord}
            ref={(m) => {  // <--- add marker refs to ref object here
              markerRefs.current[marker.id] = m;
              if (index === data.length - 1 && !done) {
                setDone(true);
              }
            }}
          >
            <Popup>{marker.content}</Popup>
          </Marker>
        );
      })}
    </MapContainer>

执行 map 语句后,您的 markerRefs 将是一个对象,其键是 id,其值是底层传单标记对象。

refs !== 状态

一旦裁判准备就绪,您将需要一种方法来采取一些行动。您可以使用 useEffect 来做到这一点。现在请记住,将 ref 放入 useEffect 依赖项数组中并不是一个好主意,因为对 ref 的更新不会触发重新渲染。因此,我创建了状态变量 done,它以 false 开始,但在地图的最后一次迭代中设置为 true。

打开弹出窗口

现在,我们创建一个在 done 为真时运行的 useEffect,这意味着引用已创建且可用:

  useEffect(() => {
    let params = new URLSearchParams(document.location.search.substring(1));
    let id = params.get("id");
    if (id && done) {
      const markerToOpen = markerRefs.current[id];
      markerToOpen.openPopup();
    }
  }, [done]);

在这个 useEffect 中,我们检查 url 参数来获取 id。如果有id,mapping和refing是done,我们就根据marker的id得到marker的ref,然后调用openPopup()

Working codesandbox

请记住,像 codesandbox 或 stackblitz 这样的沙盒类型网站有 iframe,url 参数需要在那里可用。对于该沙箱,您需要将预览弹出到它自己的 window 中,或者对 iframe 使用 url:

您会看到,当您在 url 搜索参数中指定 id 时,该标记的弹出窗口在挂载时打开。 (我没有编写任何代码来检查标记是否存在,因此如果您在该示例中尝试 id=doesnt_exist,它将中断。您需要添加额外的逻辑来检查它)。

将其与 react-router 和您要使用的其他 url 参数集成需要一些额外的工作,但这就是我将如何使用 refs 和 state 的组合来实现反应到底层传单组件并完成你想要的。

就重新渲染与许多标记一起使用而言,请记住,react-leaflet 3 已优化不是导致重新渲染,而是深入到底层传单组件并调用它们方法。您可能希望创建其他状态变量来跟踪以帮助解决此问题。例如,您可能有 const [currentOpenMarker, setCurrentOpenMarker] = useState(),并在其上放置一个 usEffect 以进入底层传单映射并调用 map.closePopup(),然后调用 currentOpenMarker.openPopup()