React Leaflet V3 自定义控件

React Leaflet V3 Custom Control

有一个新的 react-leaflet 版本即将推出,可以在这里找到: npm 安装 react-leaflet/next 文档:https://react-leaflet-v3.now.sh/docs/start-introduction

这是使用 hooks 的完整重写。

基于这些新的钩子,我正在尝试编写一个自定义层控件,我主要只是想将其分成几组并从 material-ui 渲染我的反应组件以保持地图样式与我的应用程序的其余部分内联。根据我的理解,钩子的使用方式如下:

基本控制示例

const MyControl = Control.extend({
  onAdd: map => {
    let container = DomUtil.create('div');
    container.innerHTML += 'My Control';

    return container;
  },
  // this one is optional
  onRemove: map => {},
});

const useControlElement = createElementHook(
  function createControl(_ref, ctx) {
    const context = useLeafletContext();
    var instance = new MyControl(position);
    return {
      instance: instance,
      context: Object.assign({}, ctx, {
        control: instance,
      }),
    };
  },
  function updateControl(control, props, prevProps) {},
);

const useControl = createControlHook(useControlElement);

const FinalControl = createContainerComponent(useControl);

function ControlComponent({ children }) {
  const controlRef = useRef();
  // if ref exits portal children through, note: using react scripts to support ?
   if(ref.?current?.getContainer()){
     ReactDOM.createPortal(children, ref.current.getContainer())
   }
  return <FinalControl ref={controlRef} />;
}

export default ControlComponent;

但是子项永远不会呈现,因为控件仅添加到 componentDidMount 上。

我想在 React/JSX/JS 中编写这个图层控件,我不想使用 html 即 L.Control 完全构建 uild 它并使用leaflets domutil 因为这无论如何都不适用于我的反应样式,并且提供对我的 redux 存储的受限访问。所以我试图通过一个基本控件来访问我的组件,比如 react-leaflet-control 的工作原理

我的处理方式是否正确?我只是绕着 atm 转了一圈,试图让它以一种好的方式工作。我几乎要将反应组件渲染为地图的子组件并将位置设置为绝对位置(我知道这是错误的方法,但确实有效)

<MapContainer>
  <ReactComponent />
</MapContiner>

该文档有一个自定义组件的示例,但这仅适用于已经存在的传单组件,即“核心架构”部分中的方形示例。感谢任何帮助!

我更新了我的答案。不知道对你有没有帮助,希望能给你一点提示。

我从 here 的普通 Leaflet 示例中模拟了它,它在地图的左下角添加了一个控件。

import { MapContainer, TileLayer } from 'react-leaflet';
import { useLeafletContext } from '@react-leaflet/core'
import L from 'leaflet';
import {useEffect} from 'react'

function CustomControl(props) {
    const context = useLeafletContext()

    L.Control.Watermark = L.Control.extend({
        onAdd: function (map) {
            var img = L.DomUtil.create('img');

            img.src = './logo.png';
            img.style.width = '200px';

            return img;
        },

        onRemove: function (map) {
            // Nothing to do here
        }
    });

    L.control.watermark = function (opts) {
        return new L.Control.Watermark(opts);
    }

    useEffect(() => {
        const container = context.layerContainer || context.map

        const control = L.control.watermark({ position: props.position })
        container.addControl(control)

        return () => {
            container.removeControl(control)
        }
    })

    return null
}


function MapCompenent() {

    return (
        <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={true} className='map'>
            <TileLayer
                attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
           
            {/* the custom control */}
            <CustomControl position="bottomleft"></CustomControl>
        </MapContainer>
    )
}

Demo Screenshot

感谢 Zac 的回答,我可以用这种方式做到。

依赖版本:

"react-leaflet": "^3.0.5",
"leaflet": "^1.7.1",

这是一个扩展Leaflet控件的组件。您可以使用选项通过收到的道具自定义组件。

//MyComponent.jsx
import React from 'react';
import {useLeafletContext} from '@react-leaflet/core';
import L from 'leaflet';

export const MyComponent = (props) => {
  const context = useLeafletContext();
  const control = L.control.Extend({
    //...options    
  });

  React.useEffect(() => {
    const container = context.layerContainer || context.map;
    container.addControl(control);

    return () => {
      container.removeControl(control);
    };
  });

  return null;
};

这里是自定义地图组件,您可以在其中使用之前创建的组件。

//MyMapComponent.jsx
import React from 'react';
import {MapContainer, TileLayer} from 'react-leaflet';
import {MyComponent} from './MyComponent';

export const MyMapComponent = (props) => {
  return (
    <MapContainer>
      <TileLayer url="whateverLayerURL" />
      <MyComponent {...props} />
    </MapContainer>
  );
};

这可以使用 createControlComponent 挂钩进一步简化

import { createControlComponent } from "@react-leaflet/core";
import { Control, DomUtil } from "leaflet";

Control.Watermark = Control.extend({
  onAdd: function (map) {
    const img = DomUtil.create("img");
    img.src = "./logo.png";
    img.style.width = "200px";
    return img;
  },

  onRemove: function (map) {},
});

export const WatermarkControl = createControlComponent(
  (props) => new Control.Watermark(props)
);