【问题标题】:Implementing a dynamic JSX element within a marker, using react-leaflet使用 react-leaflet 在标记内实现动态 JSX 元素
【发布时间】:2018-04-11 14:48:05
【问题描述】:

我有一个 React 应用程序,我在其中使用 Leafletreact-leaflet,这两个库都非常有用。

在这个应用程序中,我有一组坐标需要渲染如下:

  1. 缩小时,将坐标聚集到标记簇中,如下所示

  2. 放大时,每个Marker都需要有

    1. 它下面有一个动态倒数计时器
    2. 一个动态的 SVG 倒计时时钟,就像这样

对于集群,我使用react-leaflet-markercluster 插件,它非常适合显示静态内容。

但是当我需要在每个标记中显示任何动态内容时,我没有发送JSX 的选项,只能提供静态HTML,从可用的示例here 可以看出。

// Template for getting popup html MarkerClusterGroup
// IMPORTANT: that function returns string, not JSX
function getStringPopup(name) {
  return (`
    <div>
      <b>Hello world!</b>
      <p>I am a ${name} popup.</p>
    </div>
  `);
}

// that function returns Leaflet.Popup
function getLeafletPopup(name) {
  return L.popup({ minWidth: 200, closeButton: false })
    .setContent(`
      <div>
        <b>Hello world!</b>
        <p>I am a ${name} popup.</p>
      </div>
    `);
}

有没有办法处理这种情况?如何制作 JSX 标记而不是静态 HTML 标记?

PS:我已经尝试过使用 ReactDOM.renderToStringalready,但这是一个丑陋的 hack,并且每次都需要重新渲染标记。

TIA!!

这里有一个示例WebpackBin,如果您有解决方案,可以尝试一下

【问题讨论】:

  • @Vlad 实际上,我并没有考虑使用renderToString 作为解决方案。我希望在这里使用 JSX,而不是静态 HTML
  • 那么为什么不将 jsx 渲染为隐藏的(使用 css)html 并将其作为 .innerHTML 传递呢?
  • @Vlad 您能否更新提供的 bin 以显示您的意思?我不太明白
  • 哦,我的意思是回答我在上面发布链接的问题

标签: javascript reactjs leaflet jsx react-leaflet


【解决方案1】:

我现在想出了一些将自定义 JSX 渲染为标记的工作代码。

这是https://jahed.io/2018/03/20/react-portals-and-leaflet/ 95% 的副本和https://github.com/PaulLeCam/react-leaflet/blob/master/src/Marker.js 的5% 灵感

我相信有些东西可以进一步优化。

import * as React from 'react';
import { createPortal } from "react-dom";
import { DivIcon, marker } from "leaflet";
import * as RL from "react-leaflet";
import { MapLayer } from "react-leaflet";
import { difference } from "lodash";

const CustomMarker = (RL as any).withLeaflet(class extends MapLayer<any> {
    leafletElement: any;
    contextValue: any;

    createLeafletElement(props: any) {
        const { map, layerContainer, position, ...rest } = props;

// when not providing className, the element's background is a white square
// when not providing iconSize, the element will be 12x12 pixels
        const icon = new DivIcon({ ...rest, className: '', iconSize: undefined });

        const el = marker(position, { icon: icon, ...rest });
        this.contextValue = { ...props.leaflet, popupContainer: el };
        return el;
    }

    updateLeafletElement(fromProps: any, toProps: any) {
        const { position: fromPosition, zIndexOffset: fromZIndexOffset, opacity: fromOpacity, draggable: fromDraggable, className: fromClassName } = fromProps;
        const { position: toPosition, zIndexOffset: toZIndexOffset, toOpacity, draggable: toDraggable, className: toClassName } = toProps;

        if(toPosition !== fromPosition) {
            this.leafletElement.setLatLng(toPosition);
        }
        if(toZIndexOffset !== fromZIndexOffset) {
            this.leafletElement.setZIndexOffset(toZIndexOffset);
        }
        if(toOpacity !== fromOpacity) {
            this.leafletElement.setOpacity(toOpacity);
        }
        if(toDraggable !== fromDraggable) {
            if(toDraggable) {
                this.leafletElement.dragging.enable();
            } else {
                this.leafletElement.dragging.disable();
            }
        }
        if(toClassName !== fromClassName) {
            const fromClasses = fromClassName.split(" ");
            const toClasses = toClassName.split(" ");
            this.leafletElement._icon.classList.remove(
                ...difference(fromClasses, toClasses)
            );
            this.leafletElement._icon.classList.add(
                ...difference(toClasses, fromClasses)
            );
        }
    }

    componentWillMount() {
        if(super.componentWillMount) {
            super.componentWillMount();
        }
        this.leafletElement = this.createLeafletElement(this.props);
        this.leafletElement.on("add", () => this.forceUpdate());
    }

    componentDidUpdate(fromProps: any) {
        this.updateLeafletElement(fromProps, this.props);
    }

    render() {
        const { children } = this.props;
        const container = this.leafletElement._icon;

        if(!container) {
            return null;
        }

        const portal = createPortal(children, container);

        const LeafletProvider = (RL as any).LeafletProvider;

        return children == null || portal == null || this.contextValue == null ? null : (
            <LeafletProvider value={this.contextValue}>{portal}</LeafletProvider>
        )
    }
});

然后像这样在你的组件中使用它:

<Map ...>
  <CustomMarker position={[50, 10]}>
    <Tooltip>
      tooltip
    </Tooltip>
    <Popup>
      popup
    </Popup>

    <div style={{ backgroundColor: 'red' }} onClick={() => console.log("CLICK")}>
      CUSTOM MARKER CONTENT
    </div>
    MORE CONTENT
  </CustomMarker>
</Map>

如果你不使用 TypeScript。只需删除 as any: any 的东西。


编辑:某些东西会自动设置 width: 12px;height: 12px;。我还不确定,如何防止这种情况。其他一切似乎都运行良好!

EDIT2:修复它!使用iconSize: undefined


EDIT3:还有这个:https://github.com/OpenGov/react-leaflet-marker-layer 没测试过,但是示例代码看起来不错。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-26
    • 2017-05-28
    • 2015-01-29
    • 2019-12-18
    • 2020-09-07
    • 2020-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多