Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. The same question in a vanilla Leaflet context was asked here: Placing controls outside map container with Leaflet?. This is what I'm trying to accomplish within the react-leaflet framework. 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:

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:

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. (DomUtil.removeClass(this._zoomInButton, className);, etc.)

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. I know react-leaflet functions on its context provider and consumer, LeafletProvider and LeafletConsumer. When I try to call on my LeafletConsumer from within my <ControlLayer />, I get nothing back. 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 />. Do I need to export the context from the Map somehow using LeafletProvider? I am a little new to React Context API, so this is not yet intuitive for me. (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 />).

How can I properly hook this ZoomControl up to my Map component?

UPDATE:

I tried capturing the context in my redux store, and then serving it to my externalized ZoomControl. Like so:

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

This captures the context as part of my redux store. 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.

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. But I still get the same error message. Very frustrated over here.

like image 842
Seth Lutske Avatar asked Oct 20 '25 04:10

Seth Lutske


1 Answers

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:

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

For the rest take a look here: Demo

like image 181
kboul Avatar answered Oct 21 '25 18:10

kboul



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!