简体   繁体   English

如何使用 React 管理 OpenLayers Overlay 生命周期?

[英]How to manage OpenLayers Overlay lifecycle using React?

Given a react application that uses OpenLayers to render an interactive map, I want to provide tooltips for displayed features.给定一个使用 OpenLayers 渲染交互式地图的 React 应用程序,我想为显示的功能提供工具提示。 The lifecycle should be as follows: there is either no or one tooltip present on the map.生命周期应如下所示:地图上没有或只有一个工具提示。 If the user clicks within a certain distance of a feature, a tooltip appears close to that feature's position on the map and if there was a previous tooltip, it's replaced with the new one.如果用户在某个要素的特定距离内单击,工具提示会出现在该要素在地图上的位置附近,如果之前有工具提示,则会被新工具提示替换。 If a user clicks on a part of the map without a feature close by, the previous tooltip gets destroyed.如果用户单击附近没有要素的地图部分,则先前的工具提示将被破坏。

I have tried multiple ways of implementing such logic, but the final logical flaw that keeps me from succeeding is when React wants to unmount a tooltip component, it is not able to find it since OpenLayers moves it into its own ol-overlay-container container.我已经尝试了多种实现这种逻辑的方法,但最后一个让我无法成功的逻辑缺陷是当 React 想要卸载一个工具提示组件时,它无法找到它,因为 OpenLayers 将它移到了它自己的ol-overlay-container容器中.

As suggested in an answer to a similar question , a possible solution could be to use react's createPortal to directly render the tooltip into the DOM location that openlayers would copy it into anyway.正如在对类似问题的回答中所建议的那样,一个可能的解决方案是使用 react 的createPortal将工具提示直接呈现到 openlayers 无论如何都会将其复制到的 DOM 位置。 The problem with this seems to be that openlayers wraps the element nonetheless, making it impossible for react to remove it when unmounting.这个问题似乎是 openlayers 仍然包裹了元素,使得在卸载时无法做出反应来移除它。

So far, this is my code for a popup/tooltip component:到目前为止,这是我的弹出/工具提示组件代码:


import { Overlay } from "ol"
import { createRef, useEffect, useRef, useState } from "react"
import { createPortal } from "react-dom"
import useMap from "../../Hook/useMap"

const Popup = ({ feature, position, ...props }) => {
  const { map } = useMap()  // custom map context
  const overlay = useRef()
  const [domReady, setDomReady] = useState(false)
  const overlayContainer = useRef()
  const overlayContent = createRef()

  useEffect(() => {
    setDomReady(true)
    overlayContainer.current = document.getElementsByClassName(
      "ol-overlay-container ol-selectable"
    )[0]
    return () => {
      setDomReady(false)
    }
  }, [])

  useEffect(() => {
    if (domReady && overlayContainer.current) {
      overlay.current = new Overlay({
        element: overlayContainer.current,
        id: "popup",
      })
      if (position) {
        overlay.current.setPosition(position)
      }
      map.addOverlay(overlay.current)
      return () => {
        map.removeOverlay(overlay.current)
      }
    }
  }, [domReady, feature, position])

  return domReady
    ? createPortal(
        <div className="test-overlay" ref={overlayContent}>
          {props.children}
        </div>,
        overlayContainer.current
      )
    : null
}

export default Popup

This requires that the overlay container with class name ol-overlay-container ol-selectable exists during the whole lifecycle of the application.这就要求在应用程序的整个生命周期中都存在类名为ol-overlay-container ol-selectable By default, it seems it's only created once an overlay is added to the map.默认情况下,它似乎只有在将叠加层添加到地图后才会创建。 Therefore, I initialize my map with an empty overlay whose element is a div I have created in my index.html:因此,我用一个空覆盖层初始化我的地图,其元素是我在 index.html 中创建的一个div


const map = new OlMap({
  overlays: [
    new Overlay({
      id: "preserveOverlayContainer",
      element: document.getElementById("map-overlay"),
      position: [0, 0],
    }),
  ],
  ...
})

Is there no way to have a tool tip with contents managed by react, yet rendered into the map using openlayers?有没有办法让工具提示包含由 React 管理的内容,但使用 openlayers 渲染到地图中?

In the meantime, I have come up with a hacky, but seemingly working solution to this:与此同时,我想出了一个 hacky,但看似可行的解决方案:

return () => {
        map.removeOverlay(overlay.current)
        const outer = document.createElement("div")
        outer.id = "map-overlay-outer"

        document.body.appendChild(outer)

        overlayContainer.current = document.getElementById("map-overlay-outer")
      }

in the cleanup function for the second useEffect , the removeOverlay() method call removes the previously rendered tooltip entirely from the DOM, so we simply recreate it.在第二个useEffect的清理函数中, removeOverlay()方法调用将之前呈现的工具提示从 DOM 中完全移除,因此我们只需重新创建它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM