简体   繁体   English

从反应传单中的地图中删除缩放控制

[英]Remove Zoom control from map in react-leaflet

I am building a react-leaflet application, and I am trying to separate the zoom control from the map itself.我正在构建一个 react-leaflet 应用程序,并且我试图将缩放控件与地图本身分开。 The same question in a vanilla Leaflet context was asked here: Placing controls outside map container with Leaflet?在原版传单上下文中提出了同样的问题: Place controls outside map container with Leaflet? . . This is what I'm trying to accomplish within the react-leaflet framework.这就是我试图在 react-leaflet 框架内完成的。 Here is the general outline of my project:这是我的项目的大纲:

import UIWindow from './UIWindow'
import Map from './MapRL'

class App extends React.Component {
   render () {
      return (
         <div className="App">
            <Map />
            <UIWindow />
         </div>
      )
   }
}

export default App;

My map component looks like this:我的地图组件如下所示:

import React from 'react'
import { Map as LeafletMap, TileLayer } from 'react-leaflet'

class Map extends React.Component {

   render () {
      return(
         <LeafletMap
            className="sidebar-map"
            center={...}
            zoom={...}
            zoomControl={false}
            id="mapId" >

            <TileLayer
                url="..."
                attribution="...
            />

         </LeafletMap>
      )
   }
}

export default Map;

Then my UIWindow looks like this:然后我的 UIWindow 看起来像这样:

class UIWindow extends React.Component {
   render () {
      return(
         <div className="UIWindow">
            <Header />
            <ControlLayer />
         </div>
      )
   }
}

And finally, my ControlLayer (where I want my ZoomControl to live) should look something like this:最后,我的 ControlLayer(我希望 ZoomControl 存在的地方)应该如下所示:

class ControlLayer extends React.Component {
   render () {
      return (
         <div className="ControlLayer">
            <LeftSidebar />
            <ZoomControl />
            {this.props.infoPage && <InfoPage />}
         </div>
      )
   }
}

Of course with this current code, putting ZoomControl in the ControlLayer throws an error: TypeError: Cannot read property '_zoom' of undefined , with some more detailed writeup of what's wrong, with all the references the Leaflet's internal code regarding the zoom functionality.当然,使用当前的代码,将 ZoomControl 放在 ControlLayer 中会引发错误: TypeError: Cannot read property '_zoom' of undefined ,有一些更详细的错误说明,以及关于缩放功能的 Leaflet 内部代码的所有参考。 ( DomUtil.removeClass(this._zoomInButton, className); , etc.) DomUtil.removeClass(this._zoomInButton, className);等)

I expected an error, because the ZoomControl is no longer a child of the <Map /> component, but rather a grandchild of the <Map /> 's sibling.我预计会出现错误,因为 ZoomControl 不再是<Map />组件的子组件,而是<Map />的兄弟组件的孙子组件。 I know react-leaflet functions on its context provider and consumer, LeafletProvider and LeafletConsumer.我知道其上下文提供者和消费者 LeafletProvider 和 LeafletConsumer 上的 react-leaflet 函数。 When I try to call on my LeafletConsumer from within my <ControlLayer /> , I get nothing back.当我尝试从我的<ControlLayer />调用我的 LeafletConsumer 时,我一无所获。 For example:例如:

            <LeafletConsumer>
               {context => console.log(context)}
            </LeafletConsumer>

This logs an empty object.这会记录一个空对象。 Clearly my LeafletConsumer from my ControlLayer is not properly hooked into the LeaflerProvider from my <Map /> .显然,我的 ControlLayer 中的 LeafletConsumer 没有正确连接到我的<Map />的 LeaflerProvider。 Do I need to export the context from the Map somehow using LeafletProvider?我是否需要使用 LeafletProvider 以某种方式从 Map 导出上下文? I am a little new to React Context API, so this is not yet intuitive for me.我对 React Context API 有点陌生,所以这对我来说还不直观。 (Most of the rest of the app will be using React Redux to manage state changes and communication between components. This is how I plan to hook up the contents of the sidebar to the map itself. My sidebar doesn't seem to have any problem with being totally disconnected from the <Map /> ). (应用程序的其余大部分将使用 React Redux 来管理组件之间的状态更改和通信。这就是我计划将侧边栏的内容连接到地图本身的方式。我的侧边栏似乎没有任何问题与<Map />完全断开)。

How can I properly hook this ZoomControl up to my Map component?如何正确地将此 ZoomControl 连接到我的 Map 组件?

UPDATE:更新:

I tried capturing the context in my redux store, and then serving it to my externalized ZoomControl.我尝试在我的 redux 存储中捕获上下文,然后将其提供给我的外部 ZoomControl。 Like so:像这样:

            <LeafletConsumer>
               { context => {
                  this.props.captureContext(context)
               }}
            </LeafletConsumer>

This captures the context as part of my redux store.这将上下文捕获为我的 redux 存储的一部分。 Then I use this as a value in a new context:然后我将其用作新上下文中的值:

// ControlLayer.js

const MapContext = React.createContext()

            <MapContext.Provider value={this.props.mapContext}>
               <LeftSidebar />
               <MapContext.Consumer>
                  {context => {
                     if (context){
                        console.log(ZoomControl);

                     }
                  }}
               </MapContext.Consumer>
            </MapContext.Provider>

Where this.props.mapContext is brought in from my redux matchStateToProps, and its exactly the context captured by the captureContext function. this.props.mapContext是从我的 redux matchStateToProps 中引入的,它正是由 captureContext 函数捕获的上下文。

Still, this is not working.尽管如此,这还是行不通。 My react dev tools show that the MapContent.Consumer is giving the exact same values as react-leaflet's inherent '' gives when the ZoomControl is within the Map component.我的反应开发工具显示 MapContent.Consumer 给出的值与当 ZoomControl 位于 Map 组件内时 react-leaflet 的固有 '' 给出的值完全相同。 But I still get the same error message.但我仍然收到相同的错误消息。 Very frustrated over here.在这里非常沮丧。

Here is the same approach without hooks:这是没有钩子的相同方法:

the Provider should look like this:提供者应如下所示:

class Provider extends Component {
  state = { map: null };

  setMap = map => {
    this.setState({ map });
  };

  render() {
    return (
      <Context.Provider value={{ map: this.state.map, setMap: this.setMap }}>
        {this.props.children}
      </Context.Provider>
    );
  }
}

Leaflet component will be:传单组件将是:

class Leaflet extends Component {
  mapRef = createRef(null);

  componentDidMount() {
    const map = this.mapRef.current.leafletElement;
    this.props.setMap(map);
  }

  render() {
    return (
      <Map
        style={{ width: "80vw", height: "60vh" }}
        ref={this.mapRef}
        center={[50.63, 13.047]}
        zoom={13}
        zoomControl={false}
        minZoom={3}
        maxZoom={18}
      >
        <TileLayer
          attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?"
        />
      </Map>
    );
  }
}

and now to access the setMap function to the compoenntDidMount you need to do the following:现在要将 setMap 函数访问到 componentDidMount,您需要执行以下操作:

export default props => (
  <Context.Consumer>
    {({ setMap }) => <Leaflet {...props} setMap={setMap} />}
  </Context.Consumer>
);

For the rest take a look here: Demo其余的请看这里: 演示

I am not sure how to achieve that using your approach where react-leaflet's wrapper ZoomControl is not a child of Map wrapper when you try to place it outside the Map wrapper.我不确定如何使用您的方法来实现这一点,其中当您尝试将 react-leaflet 的包装器 ZoomControl 放在 Map 包装器之外时,它不是 Map 包装器的子代。

However, for a small control like the ZoomControl, an easy solution would be to create a custom Zoom component, identical to the original, construct it easily using the native css style and after accessing the map element, invoke the zoom in and out methods respectively.但是,对于像 ZoomControl 这样的小控件,一个简单的解决方案是创建一个与原始组件相同的自定义 Zoom 组件,使用原生 css 样式轻松构建它,并在访问地图元素后,分别调用放大和缩小方法.

In the below example I use react-context to save the map element after the map loads:在下面的示例中,我使用 react-context 在地图加载后保存地图元素:

useEffect(() => {
    const map = mapRef.current.leafletElement;
    setMap(map);
  }, [mapRef, setMap]);

and then here use the map reference to make a custom Zoom component identical to the native (for css see the demo):然后在这里使用地图引用使自定义缩放组件与本机相同(对于 css,请参阅演示):

const Zoom = () => {
  const { map } = useContext(Context);

  const zoomIn = e => {
    e.preventDefault();
    map.setZoom(map.getZoom() + 1);
  };
  const zoomOut = e => {
    e.preventDefault();
    map.setZoom(map.getZoom() - 1);
  };

  return (
    <div className="leaflet-bar">
      <a
        className="leaflet-control-zoom-in"
        href="/"
        title="Zoom in"
        role="button"
        aria-label="Zoom in"
        onClick={zoomIn}
      >
        +
      </a>
      <a
        className="leaflet-control-zoom-out"
        href="/"
        title="Zoom out"
        role="button"
        aria-label="Zoom out"
        onClick={zoomOut}
      >
        −
      </a>
    </div>
  );
};

and then place it wherever you like:然后把它放在任何你喜欢的地方:

const App = () => {
  return (
    <Provider>
      <div style={{ display: "flex", flexDirection: "row" }}>
        <Leaflet />
        <Zoom />
      </div>
    </Provider>
  );
};

Demo 演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM