使用geojson pointToLayer函数时,需要正确的方式来渲染传单弹出窗口中的jsx组件



嗨,有没有办法把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传单repo中有一个问题正在讨论这个问题,其结论是在bindPopup方法中简单地使用vanilla JS来创建弹出窗口。我一点也不喜欢这个解决方案,尤其是当你试图在弹出窗口中使用非常面向反应的事件处理程序(比如react redux操作(时。

当您在代码中使用React的renderToString方法时,您可能已经阅读了带有自定义React组件的React传单geojson on EachFeature弹出窗口的问题。但正如您可能已经发现的,这并不能维护您的JSX可能包含的任何交互性或JS。那里的回答者提出了使用模态而不是弹出窗口的想法,但这并不能完全回答你的问题,也不能真正在基于点层geojson的弹出窗口中使用JSX。

最终,您将无法从交互式的pointToLayer函数返回JSX。我认为这将是一个很好的功能,反应传单目前还没有实现。在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>
);
})}

工作沙箱

通过这种方式,您需要通过使用Popups手动将GeoJSON转换为Markers来更加努力,但不要像从JSX(<GeoJSON />(到普通JS(pointToLayer(再到JSX(<Popup />(那样努力。

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

我使用传单reactPopup组件的问题是,当我只是在geojson对象上进行映射时,它不会将geojson属性传递给标记层,因为react-floyemarker没有像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更新附加事件侦听器的东西来渲染所有静态内容。

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组件是静态的,所以我把它呈现为一个字符串,然后添加带有redux回调的链接作为事件监听器

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

最新更新