2

I've been working on a map component that renders points of results that stream in from search. I have gotten most of it working, until I began passing props in for the return statement. I'm using my own component, not react-leaflet's . I've noticed if I use <Map ... /> and render with props.locations.map( ... it works fine. If I reference my map = L.map(...) with <map ref={map} >and try to render <Marker .../> with props, it breaks as soon as it attempts to create the layer and add it to the map. enter image description here

The component's JS File is as follows:

    import React, {Component, useEffect, useState} from "react";
    import {Map, Marker, Popup, TileLayer} from "react-leaflet";
    import * as L from 'leaflet'
    import 'leaflet-draw'
    import 'leaflet/dist/leaflet.css';
    import 'leaflet-easybutton'
    import 'leaflet-easybutton/src/easy-button.css';
    import Style from './MapSearch.css'
    import icon from 'leaflet/dist/images/marker-icon.png';
    import iconShadow from 'leaflet/dist/images/marker-shadow.png';



    const MapSearch = (props) => {


        const [layers,setLayers] = useState([]);
        const [results,setResults] = useState([]); //Not used
        const [map,setMap]=useState({});


    


    useEffect(()=> {

        console.log('mounted');
        let map = L.map('mapsearch').setView([51.505, -0.09], 6);

        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
        }).addTo(map);

        /** Add the feature group and draw control to the map. */
        let drawnItems = new L.FeatureGroup();
        map.addLayer(drawnItems);
        const drawControl = new L.Control.Draw({
            position: 'topright',
            draw: {
                polyline: false,
                rectangle: false,
                circlemarker: false,
                polygon: false,
                circle: true,
                marker: true,
            },
            edit: {
                featureGroup: drawnItems,
                remove: true,
            },
        });
        map.addControl(drawControl);


        /** On shape drawn, add the new layer to the map. */
        map.on(L.Draw.Event.CREATED, (e) => {
            const type = e.layerType;
            const layer = e.layer;
            if (type === 'marker') {
                layer.bindPopup('popup');
            }
            console.log('LAYER ADDED:', layer);
            drawnItems.addLayer(layer);

           // this.setState({layers: drawnItems.getLayers()});
            let testArr=drawnItems.getLayers();                         /**BROKEN SECTIoN TODO */
           // setLayers(layers=> [...layers,layer]);
            //console.log({layers});

            console.log('GEO JSON', drawnItems.toGeoJSON());
            console.log(' LAYERS', drawnItems.getLayers());


        });

        L.easyButton('fa-search', () => {
            isWithinPolygon(props); //Removed because not relevant to question 

        }, 'Search Current Results within drawn layers', {position: 'topright'}).addTo(map);


        map.on(L.Draw.Event.EDITED, (e) => {
            const layers = e.layers;
            let countOfEditedLayers = 0;
            console.log('LAYER EDITED:', layers);
            layers.eachLayer((layer) => {
                countOfEditedLayers++;
            });
        });

        setMap(map); //hook to set map
        //this.setState({map: map});

         console.log("map:",{map}); }
        ,[]);



    return (

        <div id='mapsearch'>
            <map center={[0, 0]} zoom={0}>

                {props.locations.map(marker => (
                    (marker.props.isLocationEnabled === true ?
                            (marker.props.isMarkerClicked === true ?
                                    (<Marker
                                        key={marker.props.id}
                                        position={ [
                                            marker.props.lat,
                                            marker.props.long
                                        ]}
                                        opacity= '1.0'>
                                        <Popup>
                                            <b>Media ID:</b> {marker.props.values.mediaId}
                                            <br/>
                                            <b>Created Date:</b> {marker.props.values.createdDate}
                                            <br/>
                                            <b>Media URL:</b> <button onClick={()=> window.open(marker.props.values.mediaURL)}>Link</button>
                                        </Popup>
                                    </Marker>) :
                                    (<Marker
                                        key={marker.props.id}
                                        position={ [
                                            marker.props.lat,
                                            marker.props.long
                                        ]}
                                        opacity= '0.5'>
                                        <Popup>
                                            <b>Media ID:</b> {marker.props.values.mediaId}
                                            <br/>
                                            <b>Created Date:</b> {marker.props.values.createdDate}
                                            <br/>
                                            <b>Media URL:</b> <button onClick={()=> window.open(marker.props.values.mediaURL)}>Link</button>
                                        </Popup>
                                    </Marker>)
                            ) : (<div></div>)
                    )
                ))}
            </map>
        </div>
    );
};

export default MapSearch;

Index:

<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
  name="description"
  content="Media Search UI"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
  manifest.json provides metadata used when your web app is installed on a
  user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
      integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
      crossorigin=""/>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
        integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
        crossorigin=""></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>


<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.js"></script>
<link href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">

<!--
  Notice the use of %PUBLIC_URL% in the tags above.
  It will be replaced with the URL of the `public` folder during the build.
  Only files inside the `public` folder can be referenced from the HTML.

  Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
  work correctly both with client-side routing and a non-root public URL.
  Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>

Pretty stumped on this. Why can it map & render <Markers> when using <Map> but not my <map /> ?

EDIT: Screenshot of using <Map ref={map} ... /> . Adds duplicate layers, making 80% of map not draggable. enter image description here

7
  • You need to use react-leaflet's Map component or use vanilla leaflet to initialize the map. It does not make sense to initialize the map using native leaflet code and use the abstracted components then of react-leaflet. Use react-leaflet's Map component and a mapRef.
    – kboul
    Commented Sep 30, 2020 at 12:33
  • <Map ref={map}> does not fix it, it instead leads me back to an older issue I had. It seems to over-layer the map. I can see there are two zoom panels on top of each other, and it consequently disables most of the map from being draggable. When battling this issue before hand, it seems that the ref attribute wasn't working. I can see my leaflet-draw code so it must recognize it somewhat. I'll add a screenshot to the post @kboul
    – BurgMan
    Commented Sep 30, 2020 at 16:05
  • Maybe it has something to do with my first div, <div id'mapsearch' ...> If I remove it, I get the Error: Map Container not found.
    – BurgMan
    Commented Sep 30, 2020 at 16:15
  • Can you make a demo to reproduce the issue?
    – kboul
    Commented Sep 30, 2020 at 17:19
  • Unable to do that, I attempted it but don't have the slightest idea what I'm doing in codepen. If it isn't already obvious, I'm new to all of this. The code provided above will replicate the issue by changing <map> to <Map ref={map} , as well as removing the div with id 'mapsearch' (since that isn't needed as <Map> renders its own div). I Should include that I cannot do const map = this.leafletMap.leafletElement; and then reference it as ref={m => { this.leafletMap = m; }} It just has no clue what this.leafletMap is
    – BurgMan
    Commented Sep 30, 2020 at 17:57

1 Answer 1

2

So as I mentioned in the comments you should use react'leaflet's Map and use a ref to add any plugin you wish using native leaflet code.

...other imports

const mapRef = useRef();
 
<Map center={position} zoom={13} style={{ height: "100vh" }} ref={mapRef}>
          <TileLayer
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          />
         ... your code should go here regarging markers
</Map>

and then on useEffect when the component mounts take the map reference and use it accordingly to initialise any other leaflet plugin:

useEffect(() => {
    console.log("mounted");

    if (mapRef && mapRef.current) {
      const map = mapRef.current.leafletElement;

      /** Add the feature group and draw control to the map. */
      let drawnItems = new L.FeatureGroup();
      map.addLayer(drawnItems);
      const drawControl = new L.Control.Draw({
        position: "topright",
        draw: {
          polyline: false,
          rectangle: false,
          circlemarker: false,
          polygon: false,
          circle: true,
          marker: true
        },
        edit: {
          featureGroup: drawnItems,
          remove: true
        }
      });
      map.addControl(drawControl);

   ...

   setMap(map); //hook to set map
      //this.setState({map: map});

      console.log("map:", { map });
    }
  }, []);

Demo

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.