Having trouble getting a custom zoom button in React-Leaflet

I’m trying to build my own Zoom button with react-leaflet

I have the following code:

import React from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { Map, TileLayer } from 'react-leaflet';
import Control from 'react-leaflet-control';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ZoomIn from 'material-ui/svg-icons/action/zoom-in';
import ZoomOut from 'material-ui/svg-icons/action/zoom-out';


class LeafletMap extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            animate: true,
            center: [
                -34.989610443115,
                -71.232476234436
            ],
            zoom: 13,
            zoomControl: false
        };
    }

    render() {
        return (
            <div>
                <Map
                    animate={this.state.animate} 
                    center={this.state.center} 
                    zoom={this.state.zoom} 
                    zoomControl={this.state.zoomControl}
                    >
                    <TileLayer
                        url={'http://{s}.tile.osm.org/{z}/{x}/{y}.png'}
                        attribution={'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a>'}
                    />
                    <Control position="topleft">
                        <MuiThemeProvider>
                            <div>
                                <div>&nbsp;</div>
                                <FloatingActionButton mini={true}>
                                    <ZoomIn onClick={ () => alert('Zoom In') } />
                                </FloatingActionButton>
                                <div>&nbsp;</div>
                                <FloatingActionButton mini={true}>
                                    <ZoomOut onClick={ () => alert('Zoom Out') }/>
                                </FloatingActionButton>
                            </div>
                        </MuiThemeProvider>
                    </Control>
                </Map>
            </div>
        );
    }
}
export default LeafletMap;

All this is displaying good way, but now I want to to put a funcion where I can able to do zoom in or out. I don’t have an idea how to call leaflet’s methods using react-leaflet library.
I’ve tried many ways to implement it but without success.

Do you have any idea how to implement it?

Answer

There are a few ways to handle map functions/actions.

  1. Pass via props

Many options are available via the Map‘s props(bounds, center, zoom). This way allows you to hold the zoom in your one state/store, instead of in leaflet.

const React = window.React;
const {
  Map, 
  TileLayer, 
  Marker, 
  Popup
} = window.ReactLeaflet;

class ZoomInState extends React.Component {
  constructor() {
    super();
    this.state = {
      lat: 51.505,
      lng: -0.09,
      zoom: 13,
    };
    this.zoomIn = () => this.setState({zoom: this.state.zoom+1})
    this.zoomOut = () => this.setState({zoom: this.state.zoom-1})
  }

  render() {
    const position = [this.state.lat, this.state.lng];
    return ( < Map center = {
        position
      }
      zoom = {
        this.state.zoom
      }

      >
      < TileLayer attribution = '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      url = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' / >
      < Marker position = {
        position
      } >
        < Popup >
          < span >
            <button onClick={this.zoomOut}>
              Zoom out
            </button >
            < button onClick = {this.zoomIn} >
              Zoom in
            < /button>
          < /span>
        </Popup >
      < /Marker>
      </Map >
    );
  }
}
export default ZoomInState
  1. Get map via refs

This way will not hold the zoom level in your component. Often this is a good practice, because it keeps a single source of truth. You can always get the zoom with map.getZoom()

const React = window.React;
const { Map, TileLayer, Marker, Popup } = window.ReactLeaflet;

class MapByRef extends React.Component {
  constructor() {
    super();
    this.state = {
      lat: 51.505,
      lng: -0.09,
      zoom: 13,
    };
  }

  bindMap(el) {
    this.map = el.leafletElement;
  }

  zoomIn() {
    this.map.zoomIn();
  }

  zoomOut() {
    this.map.zoomOut();
  }

  render() {
    const position = [this.state.lat, this.state.lng];
    return (
      <Map center={position} zoom={this.state.zoom} ref={::this.bindMap}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
        />
        <Marker position={position}>
          <Popup>
            <span>
              <button onClick={::this.zoomIn} >Zoom In</button>
              <button onClick={::this.zoomIn} >Zoom Out</button>
            </span>
          </Popup>
        </Marker>
      </Map>
    );
  }
}
export default MapByRef

3. Get from Context

This way is nice if you want to make many child components that need to interact with the map. It also keeps leaflet as the single source of truth.

const React = window.React;
const { Map, TileLayer, Marker, Popup } = window.ReactLeaflet;

class CustomMarker {

  zoomIn(){
    this.context.map.zoomIn()
  }
  zoomOut(){
    this.context.map.zoomOut()
  }

  render() {
    return (
      <Marker position={position}>
        <Popup>
          <button onCLick={::this.zoomIn}>Zoom In</button>
          <button onCLick={::this.zoomOut}>Zoom In</button>
        </Popup>
      </Marker>
    )
  }
}
export CustomMarker


class MapWithCustomChildren extends React.Component {
  constructor() {
    super();
    this.state = {
      lat: 51.505,
      lng: -0.09,
      zoom: 13,
    };
  }

  render() {
    const position = [this.state.lat, this.state.lng];
    return (
      <Map center={position} zoom={this.state.zoom}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
        />
        <CustomMarker />
      </Map>
    );
  }
}
export default MapWithCustomChildren