首页 > 解决方案 > React-Leaflet:将地图控制组件放置在地图之外?

问题描述

这是我的另一个问题的更通用版本:Remove Zoom control from map in react-leaflet。React-leaflet 带有一些控制地图的组件 - 即<ZoomControl />,<LayersControl />等。但是为了使这些组件与地图实例正确通信,它们必须编写为组件的子<Map />组件,如下所示:

<Map center={...} zoom={...}>

  <ZoomControl />
  <LayersControl />
  <MyCustomComponent />

</Map>

我正在尝试创建一种情况,即不是地图直接子级的地图组件可以与地图正确通信。例如:

<App>
  <Map />
  <Sibling>
    <Niece>
      <ZoomControl />
      <OtherControls />
    </Niece>
  </Sibling>
</App>

显然这里的问题是,当这些控件不再是 的子控件或后代时<Map />,它们不会通过提供者接收地图实例。正如您在我的另一个问题中看到的那样,我尝试创建一个新的 Context 对象并使用它来将地图提供给移位的控件。那没有用,我不知道为什么。到目前为止,我的解决方案是使用 vanilla javascript 将这些控件重新安置在componentDidMount其预期的父级中,如下所示:

// Niece.js
componentDidMount(){
  const newZoomHome = document.querySelector('#Niece')
  const leafletZoomControl= document.querySelector('.leaflet-control-zoom')
  newZoomHome.appendChild(leafletZoomControl)
}

我真的很讨厌这个,因为现在我的一般组件结构并没有反映应用程序结构。我的 Zoom 需要作为我的地图的一部分编写,但最终会出现在我的 Neice 组件中。

Kboul 在我的另一个问题中的解决方案只是从头开始重建缩放组件并将其提供给地图上下文。这适用于简单的缩放组件,但对于更复杂的组件,我无法重建整个框架。例如,我制作了esri-leaflet 的 geosearch的快速 react-leaflet 组件版本:

import { withLeaflet, MapControl } from "react-leaflet";
import * as ELG from "esri-leaflet-geocoder";

class GeoSearch extends MapControl {
  createLeafletElement(props) {
    const searchOptions = {
       ...props,
      providers: props.providers ? props.providers.map( provider => ELG[provider]()) : null
    };

    const GeoSearch = new ELG.Geosearch(searchOptions);
    // Author can specify a callback function for the results event
    if (props.onResult){
      GeoSearch.addEventListener('results', props.onResult)
    }
    return GeoSearch;
  }

  componentDidMount() {
    const { map } = this.props.leaflet;
    this.leafletElement.addTo(map);
  }
}

export default withLeaflet(GeoSearch);

<Map />它相对简单,并且在组件内声明时效果很好。但我想将它移动到应用程序中的单独位置,并且我不想重新编码整个 esri Geosearch。如何在组件外部使用功能正常的 react-leaflet 控制组件,<Map />同时将其正确链接到地图实例?

如果您愿意,这里有一个快速的codsandbox 模板,可以开始搞乱。谢谢阅读。

标签: javascriptreactjscustom-componentreact-contextreact-leaflet

解决方案


您可以使用onAdd方法在地图外为您的插件创建一个容器,然后使用 refs 将元素添加到 DOM,如下所示:

class Map extends React.Component {
  mapRef = createRef();
  plugin = createRef();

  componentDidMount() {
    // map instance
    const map = this.mapRef.current.leafletElement;

    const searchcontrol = new ELG.Geosearch();
    const results = new L.LayerGroup().addTo(map);
    searchcontrol.on("results", function(data) {
      results.clearLayers();
      for (let i = data.results.length - 1; i >= 0; i--) {
        results.addLayer(L.marker(data.results[i].latlng));
      }
    });
    const searchContainer = searchcontrol.onAdd(map);
    this.plugin.current.appendChild(searchContainer);
  }

  render() {
    return (
      <>
        <LeafletMap
          zoom={4}
          style={{ height: "50vh" }}
          ref={this.mapRef}
          center={[33.852169, -100.5322]}
        >
          <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        </LeafletMap>
        <div ref={this.plugin} />
      </>
    );
  }
}

演示


推荐阅读