使用 geojson pointToLayer 函数时需要正确的方法在 Leaflet 弹出窗口中呈现 jsx 组件

Need proper way to render jsx component inside Leaflet popup when using geojson pointToLayer function

您好,有没有什么方法可以将 jsx 组件传递给 bindPopup 函数,这样我就可以在单击按钮时推送 redux 命令?

 pointToLayer={(
      geoJsonPoint: Feature<Point, DeviceProperties>,
      latlng,
    ) => {
      const marker = L.marker(latlng);
      marker.setIcon(
        markerIcon({ variant: geoJsonPoint.properties.relation }),
      );

      const sddds = (
        <div className="font-quicksand">
          <h2>{geoJsonPoint.properties.id}</h2>
          <h2>{geoJsonPoint.properties.name}</h2>
          <p>{geoJsonPoint.properties.description}</p>
          <p>{geoJsonPoint.properties.ownerId}</p>
          <a
            onClick={() => {
              dispatch(setDevice(geoJsonPoint.properties));
            }}
          >
            Open device details
          </a>
        </div>
      );

      marker.bindPopup(renderToString(sddds));
      return marker;
    }}

我知道我可以使用 React 传单组件,但那样我就无法将道具传递到每个标记选项(我的意思是标记作为图层)。

所以这已经讨论了一下。 react-leaflet repo 中有 an issue 讨论这个,其结论是在 bindPopup 方法中简单地使用 vanilla JS 来创建你的弹出窗口。我根本不喜欢这种解决方案,尤其是当您尝试在弹出窗口中使用非常面向反应的事件处理程序(如反应-redux 操作)时。

当您在代码中使用 React 的 renderToString 方法时,您可能已经阅读了 问题。但是正如您可能已经发现的那样,这不会保留您的 JSX 可能包含的任何交互性或 JS。那里的回答者提出了使用模式而不是弹出窗口的想法,但这并不能完全回答你的问题,也不能真正在基于点层 geojson 的弹出窗口中使用 JSX。

最终,您将无法从 pointToLayer 交互功能中 return JSX。我认为这将是 react-leaflet 目前没有实现的一个很好的特性。在 pointToLayer 函数的闭包中,没有好的方法可以直接编写功能齐全的 JSX。

我玩了一会儿,尝试利用 pointToLayer 并将每次迭代的 feature 保存到状态,然后用 Popup 渲染 Marker从那开始,但它让我思考 - 为什么要打扰?完全放弃 GeoJSON 组件,直接从 JSON 对象渲染 Markers 和 Popups。像这样:

{myGeoJson.features.map((feature, index) => {
  return (
    <Marker
      key={index}
      position={L.latLng(feature.geometry.coordinates.reverse())}
    >
      <Popup>
        <button
          onClick={() => { yourReduxAction() }}
        >
          Click meeee
        </button>
      </Popup>
    </Marker>
  );
})}

Working sandbox

在这种情况下,您需要用 Popups 手动将 GeoJSON 转换为 Markers从 JSX (<GeoJSON />) 到 vanilla JS (pointToLayer) 回到 JSX (<Popup />).

这是我想到的两个解决方案,如果有人遇到同样的问题,我想与大家分享。

我使用 leaflet-react Popup 组件的问题是,当我只映射 geojson 对象时,它不会将 geojson 属性传递给标记层,因为 react-leaflet Marker 没有像 geojson 层那样的 api 功能,我需要通过地图其他部分的标记层访问这些属性。

解决方案 1:

在 pointToLayer 方法中使用 ReactDOM.render(),React 将显示有关纯函数的警告,但它会起作用。您只是不应该渲染导入的组件,因为它会抱怨存储和 redux 提供程序,而是将组件代码粘贴到渲染中。如果您想避免警告,请创建另一个函数/挂钩并将其 useEffect() 中的代码渲染到容器(div 或其他内容)。

示例如下:

const popup = L.popup();
const marker = L.marker(latlng);
const container = L.DomUtil.create('div');
render(
  <div>
    <h2>{props.id}</h2>
    <h2>{props.name}</h2>
    <p>{props.description}</p>
    <p>{props.ownerId}</p>
    <a onClick={() => dispatch(setDevice(geoJsonPoint.properties))}></a>
  </div>,
  container,
);
popup.setContent(container);
marker.bindPopup(popup);
return marker;

带有自定义挂钩/函数:

const useRenderPopup = (props) => {
const container = L.DomUtil('div');
const dispatch = useAppDispatch()
useEffect(() => {
render(
  <div>
    <h2>{props.id}</h2>
    <h2>{props.name}</h2>
    <p>{props.description}</p>
    <p>{props.ownerId}</p>
    <a onClick={() => dispatch(setDevice(props.geoJsonPoint.properties))}></a>
  </div>,
  container,
);
},[])
return container;
}

然后像popup.setContent(useRenderPopup(someprop))那样调用这个函数,这样就不会出现警告了。

解决方案 2:

使用 renderToString() 和其他需要触发 redux update 附加事件侦听器的东西渲染所有静态内容。

const popup = L.popup();
const marker = L.marker(latlng);
const link = L.DomUtil.create('a');
const container = L.DomUtil.create('div');
const content = <DeviceSummary {...geoJsonPoint.properties} />;

marker.setIcon(markerIcon({ variant: geoJsonPoint.properties.relation }));

link.addEventListener('click', () =>
  dispatch(setDevice(geoJsonPoint.properties)),
);

link.innerHTML = 'Show device details';
container.innerHTML = renderToString(content);
container.appendChild(link);

popup.setContent(container);
marker.bindPopup(popup);
return marker;

这里的 DeviceSummary 组件是静态的,所以我将它呈现为一个字符串,然后附加 link 并将 redux 回调添加为它的事件侦听器。

(除自定义函数示例外的两种解决方案都进入 geoJSON 层内的 pointToLatyer 方法)