React-leaflet: How to write a component which outputs Markers?

Created on 22 Nov 2015  路  12Comments  路  Source: PaulLeCam/react-leaflet

How a custom react component which outputs Marker on map should be written? I need to have smth like this working:

import MyMarker from './MyMarker.js';

const builtMarker = (function() {
  const position = [51.520, -0.11];
  return (
    <MyMarker position={position}/>
  );
})();

render(
  <div>
    <h1>Hello, world!</h1>
    <div className="map">
        <Map center={center} zoom={13}>
            <TileLayer
            url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            />
            {builtMarker}
        </Map>
    </div>
  </div>
  ,
  document.getElementById('example')
);

I made MyMarker component like that https://github.com/varya/react-leaftlet-test/blob/master/src/MyMarker.js but this gets error:

Uncaught TypeError: Cannot read property 'addLayer' of undefined

I guess the component should not only extend MapLayer but also provide special interface. What is missing? I could not find similar example in the docs.

Also, what should I do to output several Markers? I mean, in React it's required to be in a single wrapper. But it cannot be just <div> for the map. How this wrapper should be written?

PS: This is the repo where I demonstrate my case https://github.com/varya/react-leaftlet-test

Most helpful comment

What do you mean by variable and set of markers?
At this point it is just React, so you can apply usual patterns that work with it.

Ex:

const MyPopupMarker = ({ map, position, children }) => (
  <Marker map={map} position={position}>
    <Popup>
      <span>{children}</span>
    </Popup>
  </Marker>
);

const MyMarkersList = ({ map, markers }) => {
  const items = markers.map(({ key, ...props }) => (
      <MyPopupMarker key={key} map={map} {...props} />
  ));
  return <div style={{display: 'none'}}>{items}</div>;
};

...

class MyMap extends Component {
  ...
  render() {
    // Probably generated dynamically, but the logic is the same                            
    const markers = [
      {key: 'marker1', position: [51.5, -0.1], children: 'My first popup'},
      {key: 'marker2', position: [51.51, -0.1], children: 'My second popup'},                  
    ];

    return (
      <Map ...>
        <TileLayer .../>
        <MyMarkersList markers={markers} />
      </Map>
    );
  }
}

The only constraint if you want to render a list in a component is to wrap it in a container, this is purely a React constraint.
In our case having Leaflet actually render the layers, a possible solution is to wrap them in a div with display: none.

All 12 comments

I don't know if it's the right approach in React world, but I did the following and it worked:

import React, {Component} from 'react';
import {Marker, Popup} from 'react-leaflet';

class MyMarker extends Marker {

  componentWillMount() {
    super.componentWillMount();
  }

  render () {
    return (
        <Marker position={this.props.position}>
            <Popup>
                <span>A pretty CSS3 popup.<br/>Easily customizable.</span>
            </Popup>
        </Marker>
    )
  }
}

export default MyMarker;

You were simply missing map, that's passed via const { map, position, ...props } = this.props; in Marker.js

@surkova, this does not help. But I updated the repo with componentWillMount anyway, just to demonstrate that this does not work.

Then yet another suggestion:

import React, {Component} from 'react';
import {marker} from 'leaflet';
import {MapLayer} from 'react-leaflet';

class MyMarker extends MapLayer {
  componentWillMount() {
    super.componentWillMount();
    const { map, position, popupText, ...props } = this.props;
    this.leafletElement = marker(position, props).bindPopup(popupText);
  }
  render() {
    return this.renderChildrenWithProps({
      layerGroup: this.leafletElement,
    });
  }
}

export default MyMarker;
const builtMarker = (function() {
  const position = [51.520, -0.11];
  const popupText = "A pretty CSS3 popup.<br/>Easily customizable."
  return (
    <MyMarker position={position} popupText={popupText}/>
  );
})();

Thank you very much, but the point is to use Marker react component. Of course it's possible to create markers on the low level, but then the usage of react-leaflet doesn't make any sense.

As written in the documentation, you need to pass the map property injected by the <Map> component to children components.

Ex:

const MyMarker = ({ map, position }) => (
  <Marker map={map} position={position}>
    <Popup>
      <span>A pretty CSS3 popup.<br/>Easily customizable.</span>
    </Popup>
  </Marker>
);

...

<Map center={center} zoom={13}>
  <TileLayer
    url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
    attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  />
  <MyMarker position={position} />
</Map>

@PaulLeCam, thank you for the answer. This example works. But I'm also wondering how to insert a variable or a set of Markers. This would be nice to have an example in examples with a custom component which outputs a list of markers. I think many will be clear from such an example.

What do you mean by variable and set of markers?
At this point it is just React, so you can apply usual patterns that work with it.

Ex:

const MyPopupMarker = ({ map, position, children }) => (
  <Marker map={map} position={position}>
    <Popup>
      <span>{children}</span>
    </Popup>
  </Marker>
);

const MyMarkersList = ({ map, markers }) => {
  const items = markers.map(({ key, ...props }) => (
      <MyPopupMarker key={key} map={map} {...props} />
  ));
  return <div style={{display: 'none'}}>{items}</div>;
};

...

class MyMap extends Component {
  ...
  render() {
    // Probably generated dynamically, but the logic is the same                            
    const markers = [
      {key: 'marker1', position: [51.5, -0.1], children: 'My first popup'},
      {key: 'marker2', position: [51.51, -0.1], children: 'My second popup'},                  
    ];

    return (
      <Map ...>
        <TileLayer .../>
        <MyMarkersList markers={markers} />
      </Map>
    );
  }
}

The only constraint if you want to render a list in a component is to wrap it in a container, this is purely a React constraint.
In our case having Leaflet actually render the layers, a possible solution is to wrap them in a div with display: none.

Thank you very much! Now it's clear.
I made a pull request with this code into your set of examples. This is the place where I was looking for an answer before asked here. Maybe someone else will also look there :-)
https://github.com/PaulLeCam/react-leaflet/pull/104

Glad it helps!
Thanks for your PR, I'll merge it.

map and layerContainer is not passed to child componet in react-leaflet 1.1.1 version ???

I have set up map like this

render(){
     return(
    <Map  className="markercluster-map"  ref = "map" maxZoom = {30} minZoom ={2} center={this.props.markers[0].position}  zoom={9}>
          <BingTileLayer url = '' bingKey='<bingmapskey>'  type='aerial' />
           {
            this.props.markers?
              <MarkerClusterGroup   wrapperOptions={{enableDefaultStyle: true}}  >
                 <GeoJSON    ref='geojd' data = {this.geoj}  onEachFeature = {this.onEachDot }/>
            </MarkerClusterGroup>
            :
            null
        }



      </Map>
     ) }

What i require is to have access to my GeoJSON Layer via

componentDidMount(){
let th = this;
this.map = this.refs.map.leafletElement;

   this.geojson = this.refs.geojd.leafletElement 
        console.log(this.geojson);           

     }

I have a question in this regard.
I have 20 different sets of markers, with each set comprising of minimum 50 markers (in production may be much more than that).
In my current application when I iterate over these sets of markers for rendering, it freezes my UI causing frustration to the end user.

How can I render these sets on leaflet map asynchronously. What is the best approach?
And also, can the leaflet map _L.map_ instance be approached by multiple simultaneously executing blocks of code?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thenickcox picture thenickcox  路  4Comments

kaitlynbrown picture kaitlynbrown  路  3Comments

Shadowman4205 picture Shadowman4205  路  4Comments

robinmetral picture robinmetral  路  4Comments

mrafei picture mrafei  路  4Comments