将 React 组件渲染为带有工作事件/交互的 React-leaflet 中的自定义标记图标

问题描述 投票:0回答:1
    const IconGen = ({ incomingData }) => {
     
      const [dataMap, setDataMap] = useState({});
      const dataMapTemp = {}; 
      incomingData.BandData.forEach((each) => {
        dataMapTemp[each.AntennaX].list.push(each);
        dataMapTemp[each.AntennaX].angle= each.angle;
        dataMapTemp[each.AntennaX].sId = each.SID;
          });
      if (!dataMap[1]) {
 
        setDataMap(dataMapTemp);
       
      }

      return (
         <div class="container_Sector"  >
             <div class="circle" style={{backgroundColor : "#c31605"></div> 
                 <div 
              id = {dataMap && dataMap[1]?.sId } 
              
              style={{
                rotate: `${dataMap[1]?.angle}deg`,
              }}
              onClick={() => {
                alert("You clicked the sector!");
              }
              }
            >
              {dataMap[1]?.list.map((each) => generateSvg(each))}
            </div>
                 
            <div
              style={{
                rotate: `${dataMap[2]?.angle}deg`,
              }}
            >
              {dataMap[2]?.list.map((each) => generateSvg(each))}
            </div>
            <div 
              style={{
                rotate: `${dataMap[3]?.angle}deg`,
              }}
            >
              {dataMap[3]?.list.map((each) => generateSvg(each))}
            </div>
          </div>
        
      );
    };
    export default IconGen;

    //Parent Component

    <MapContainer>
       <Marker
               key={data.SiteID}
               position={[data.Latitude, data.Longitude]}
               icon = <IconGen
                incomingData={data}
     
              />
               
             >
     
             </Marker>
    </Mapcontainer>

我可以使用

icon={L.divIcon({ className: "custom icon", html: ReactDOMServer.renderToString( <MyComponent/> ) })}
渲染自定义图标。

但是,自定义图标组件中的

onClick
不会触发。由于使用
onClick
渲染 MyComponent,
ReactDOMServer.renderToString
无法工作。

我需要自定义组件内的

onClick
事件才能正常运行。

javascript reactjs onclicklistener react-leaflet leaflet.markercluster
1个回答
0
投票

onClick
处理程序不起作用的原因是因为
renderToString
意味着组件没有真正安装,从某种意义上说,React没有意识到它是一个持续存在的问题。运行
renderToString
将组件减少到该组件在第一次渲染时的 DOM 外观,并且从调用
renderToString
的点起,没有其他任何东西会改变它。

这里的基本问题是

react-leaflet
库不支持开箱即用。但是,我们可以通过以下方式解决这个问题:

  1. 使用
    L.divIcon
    将虚拟 div 渲染为图标,其中不包含任何内容。我们将为每个标记的 div 分配一个唯一的 ID,稍后会派上用场。
  2. 使用
    Marker
    组件
    add
    事件来检测图标何时实际渲染到 DOM。
  3. 发生这种情况时,通过使用以上述唯一 ID 为目标的 div 的 React portal 在虚拟标记内渲染真实的 React 组件。
  4. 另外检测标记何时被移除并移除传送门。

我们可以将此行为封装在

EnhancedMarker
组件中,以方便使用。

这是概念验证的有效 CodeSandbox。在这个概念证明中,我将两个按钮渲染为标记,它们每个都有有效的单击事件。

以下包括可应用于任何情况的通用代码:

import React, { useState, useId, useMemo } from "react";
import { createPortal } from "react-dom";
import { MapContainer, TileLayer, Marker } from "react-leaflet";

// `EnhancedMarker` has the same API as `Marker`, apart from the `icon` can be a React component.
const EnhancedMarker = ({
  eventHandlers,
  icon: providedIcon,
  ...otherProps
}) => {
  const [markerRendered, setMarkerRendered] = useState(false);
  const id = "marker-" + useId();

  const icon = useMemo(
    () =>
      L.divIcon({
        html: `<div id="${id}"></div>`,
      }),
    [id]
  );

  return (
    <>
      <Marker
        {...otherProps}
        eventHandlers={{
          ...eventHandlers,
          add: (...args) => {
            setMarkerRendered(true);
            if (eventHandlers?.add) eventHandlers.add(...args);
          },
          remove: (...args) => {
            setMarkerRendered(false);
            if (eventHandlers?.remove) eventHandlers.remove(...args);
          },
        }}
        icon={icon}
      />
      {markerRendered &&
        createPortal(providedIcon, document.getElementById(id))}
    </>
  );
};

const MarkerIconExample = () => {
  return (
    <>
      <button onClick={() => console.log("button 1 clicked")}>Button 1</button>
      <button onClick={() => console.log("button 2 clicked")}>Button 2</button>
    </>
  );
};

const CENTER = [51.505, -0.091];
const ZOOM = 13;
const App = () => {
  return (
    <MapContainer center={CENTER} zoom={ZOOM}>
      <TileLayer
        attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
      />
      <EnhancedMarker position={CENTER} icon={<MarkerIconExample />} />
    </MapContainer>
  );
};


对于您自己的示例,您应该能够:

  1. 复制新的
    EnhancedMarker
    组件。
  2. 将用例中
    <Marker>
    的现有用法更改为
    <EnhancedMarker>
  3. 只需在
    <IconGen />
    <EnhancedMarker>
    属性中使用
    icon
© www.soinside.com 2019 - 2024. All rights reserved.