Git Product home page Git Product logo

react-leaflet-universal's Introduction

react-leaflet-universal

Thin wrapper around react-leaflet that is designed to make it easier to implement the module in universal applications. Leaflet was not designed with the server in mind, making it very difficult to work with for server-side rendering.

To sidestep this issue, we simply don't render server side. This module wraps all of react-leaflet's components in a Wrapper class that only renders when the component is mounted - which only happens client-side.

usage

Just use it as you normally would use react-leaflet. e.g. Instead of

import { Map } from 'react-leaflet';

write

import { Map } from 'react-leaflet-universal';

To forward reference, pass to leafletRef:

<Map leafletRef={ref}>
  <TileLayer ... />
</Map>

If you do not provide leafletRef, wrappers will instead create their own ref and set the property leafletElement on the instance when it becomes available, so setting a ref prop will still work, however note that since this only occurs late in the render cycle, leafletElement may still be undefined when attempting to access it from the ref, so it is recommended to check that ref.leafletElement exists before attempting to invoke properties or methods on it.

Troubleshooting custom react-leaflet components / render prop support

Some components, such as react-leaflet-markercluster, make use of componentWillMount and so cannot be used directly.

To mitigate this, you can now use a function render prop instead of normal children for a component. Thus, instead of e.g.

<Map>
  <TileLayer ... />
</Map>

this will also work

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

So in the case of react-leaflet-markercluster, you can write something similar to:

<Map>
  () => {
    const MarkerClusterGroup = require('react-leaflet-markercluster').default;
    return (
      <div>
        <TileLayer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        <MarkerClusterGroup
          markers={[
            { position: [49.8397, 24.0297] },
            { position: [52.2297, 21.0122] },
            { position: [51.5074, -0.0901] },
          ]}
        />
      </div>
    );
  }
 </Map>

(contrast with the example on the react-leaflet-markercluster website)

Note: If you use React 16.2+, you can also make use of <Fragment></Fragment> or simply <></> instead of the wrapping <div> in the example above.

react-leaflet-universal's People

Contributors

alexismoreau avatar dependabot[bot] avatar elkorn avatar gromchen avatar masotime avatar sairus2k avatar sdost avatar sharils avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-leaflet-universal's Issues

How to get leaflet object of the map inside an extending class

how to get leaflet object of the map inside custom class?

Because I'm using https://github.com/YUzhva/react-leaflet-markercluster and I've forked it to use your component but it throws this error: Uncaught TypeError: Cannot read property '_container' of undefined.

It seems that LayerGroup can't access to the map container from the context.

Here is my code:

import React, {Children, cloneElement} from 'react';
import PropTypes from 'prop-types';
import {LayerGroup} from 'react-leaflet-universal';

let L;

export default class MarkerClusterGroup extends LayerGroup {

  constructor() {
    super();
    this.state = { loaded: false };
  }

  componentDidMount() {
	  L = require('leaflet');
	  require('leaflet.markercluster');
	  this.setState({ loaded: true }, () => {
		  // Override auto created leafletElement with L.markerClusterGroup element
		  this.leafletElement = L.markerClusterGroup(this.props.options);

		  console.log(this.context);

		  if (this.props.markers.length) {
			  this.addLayersWithMarkersFromProps(this.props.markers);
		  }

		  this.props.wrapperOptions.enableDefaultStyle && (
			  this.context.map._container.className += ' marker-cluster-styled'
		  );

		  !this.props.wrapperOptions.disableDefaultAnimation && (
			  this.context.map._container.className += ' marker-cluster-animated'
		  );

		  // Init listeners for markerClusterGroup leafletElement only once
		  this.initEventListeners(this.leafletElement);
    });
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.markers.length && !isArraysEqual(this.props.markers, nextProps.markers)) {
      // Remove layer from map with previously rendered clustered markers
      this.layerContainer.removeLayer(this.leafletElement);
      // Remove layers with markers from markerClusterGroup
      this.leafletElement.clearLayers();

      this.addLayersWithMarkersFromProps(nextProps.markers);
    }
  }

  removeMarkersWithSameCoordinates(markers) {
    // init filtered markers list with first marker from list
    let filteredMarkers = [markers[0]];

    markers.forEach((marker) => {
      if (!JSON.stringify(filteredMarkers).includes(JSON.stringify(marker))) {
        filteredMarkers.push(marker);
      }
    });

    return filteredMarkers;
  }

  addLayersWithMarkersFromProps(markers) {
    let markersOptions = this.props.markerOptions
      ? Object.assign({}, this.props.markerOptions)
      : {};

    let filteredMarkers = this.props.wrapperOptions.removeDuplicates
      ? this.removeMarkersWithSameCoordinates(markers)
      : markers;

    let leafletMarkers = [];

    filteredMarkers.forEach((marker) => {
      let currentMarkerOptions = marker.options
        ? Object.assign({}, marker.options)
        : null ;

      let leafletMarker = L.marker(
        [marker.lat, marker.lng],
        currentMarkerOptions || markersOptions
      );

      marker.popup && leafletMarker.bindPopup(marker.popup);
      marker.tooltip && leafletMarker.bindTooltip(marker.tooltip);

      leafletMarkers.push(leafletMarker);
    });

    // Add markers leafletElements to the markerClusterGroup
    this.leafletElement.addLayers(leafletMarkers);
    // Add clustered markers to the leaflet map
    !this.props.children && this.layerContainer.addLayer(this.leafletElement);
  }

  initEventListeners(markerClusterGroup) {
    this.props.onMarkerClick && (
      markerClusterGroup.on('click', (marker) => {
        this.props.onMarkerClick(marker.layer);
      })
    );

    this.props.onClusterClick && (
      markerClusterGroup.on('clusterclick', (cluster) => {
        this.props.onClusterClick(cluster.layer);
      })
    );

    this.props.onPopupClose && (
      markerClusterGroup.on('popupclose', (map) => {
        this.props.onPopupClose(map.popup);
      })
    );
  }

  addLayersWithReactLeafletMarkers() {
    const leafletMarkers = [];

    // Map through all react-leaflet Markers and clone them with ref prop
    // ref prop required to get leafletElement of Marker
    return Children.map(this.props.children, (reactLeafletMarker, index) => (
      cloneElement(reactLeafletMarker, {
        ref: (marker) => {
          if (marker) {
            leafletMarkers.push(marker.leafletElement);

            if (
              (index === (this.props.children.length - 1)) ||
              // addClusteredMarkersToMap when there is only one marker
              !Array.isArray(this.props.children)
            ) {
              // Add markers leafletElements to the markerClusterGroup
              this.leafletElement.addLayers(leafletMarkers);
              // Add clustered markers to the leaflet map
              this.layerContainer.addLayer(this.leafletElement);
            }
          }
        },
        key: `react-leaflet-marker-${index}`
      })
    ));
  }

  getLeafletElement() {
    return this.leafletElement;
  }

  render() {
	  if (!this.state.loaded) {
		  return null;
	  }

    return this.props.children
    ? (
      <section className="marker-cluster-group">
        {this.addLayersWithReactLeafletMarkers()}
      </section>
    )
    : null;
  }
}

function isArraysEqual(firstArray, secondArray) {
  return (JSON.stringify(firstArray) === JSON.stringify(secondArray));
}

MarkerClusterGroup.propTypes = {
  // List of markers with required lat and lng keys
  markers: PropTypes.arrayOf(PropTypes.object),
  // List of react-leaflet markers
  children: PropTypes.node,
  // All available options for Leaflet.markercluster
  options: PropTypes.object,
  // All available options for Leaflet.Marker
  markerOptions: PropTypes.object,
  // Options that are supporting by react-leaflet-markercluster wrapper
  wrapperOptions: PropTypes.object,
  // Events
  onMarkerClick: PropTypes.func,
  onClusterClick: PropTypes.func,
  onPopupClose: PropTypes.func
};

MarkerClusterGroup.defaultProps = {
  markers: [],
  wrapperOptions: {}
};

error Module not found: Error: Can't resolve 'leaflet.markercluster'

hello i've this but i install module

ERROR in ./node_modules/react-leaflet-markercluster/dist/react-leaflet-markercluster.min.js
Module not found: Error: Can't resolve 'leaflet.markercluster' in 'D:\project\react-starterkit\node_modules\react-leaflet-markercluster\dist'
 @ ./node_modules/react-leaflet-markercluster/dist/react-leaflet-markercluster.min.js 1:1613-1645

react-leaflet-universal breaks with react-leaflet 3

On a new project i was using react-leaflet 3 and react leaflet universal on next.js
got a immediate ``Check the render method of `LeafletUnivMap``` when trying to use

It seems react-leaflet-universal breaks with some specific versions of react-leaflet itself

package.json

"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-leaflet": "^3.0.2",
"react-leaflet-markercluster": "^3.0.0-rc1",
"react-leaflet-universal": "^2.2.1",

Workarounds

reverting to "react-leaflet": "^2.8.0" fixes the issue

Screenshots

Screenshot from 2020-11-27 12-44-12

System

  • OS: Ubuntu 20.04
  • Version of Next.js: v10.0.2
  • Version of Node.js: v12.12.0
  • Browser: Chrome

Marker component doesn't work?

I tried to execute this basic example from react-leaflet package but the Marker didn't appear on the Map. Does the Marker component work with react-leaflet-universal? Thanks!

Maintenance state of this repo

I tried this library and was wondering about the maintenance status. There are several issues which prevent me from using this library and I'm not quite sure if this project is actively maintained.

Some issues:

It looks like "babel-runtime" is required somewhere but not listed in dependencies or peerDependencies.

 Error: Cannot find module 'babel-runtime/helpers/extends'

Version "^6" is declared as devDependency so I installed it on to of my babel@7 setup (which is rather ugly)

{
    "@babel/core": "7.2.0",
    "babel-runtime": "6.26.0"
}

After this I ran into more issues and eventually gave up:

decorator.js:60 Uncaught TypeError: Cannot assign to read only property 'displayName' of function 'class ProxyComponent extends InitialParent {
    constructor(props, context) {
      super(props, context)
    ...<omitted>... }'
    at new Decorated (decorator.js:60)
    at new LeafletUnivMap (eval at ../../node_modules/react-hot-loader/dist/react-hot-loader.development.js (react-hot-loader.development.js:322), <anonymous>:5:7)
    at constructClassInstance (react-dom.development.js:12484)
    at updateClassComponent (react-dom.development.js:14255)
    at beginWork (react-dom.development.js:15082)
    at performUnitOfWork (react-dom.development.js:17820)
    at workLoop (react-dom.development.js:17860)
    at HTMLUnknownElement.callCallback (react-dom.development.js:149)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
    at invokeGuardedCallback (react-dom.development.js:256)

Removing the hot loader mechanism did not help fixing this issue (not an option anyways)

LayersControl Error

I am getting an error when attempting to use LayersControl.BaseLayer or LayersControl.Overlay.

 <Map center={ [51.505, -0.09] } zoom={ 13 } id="leaflet-map">
    <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
    <LayersControl position="topright">
        <LayersControl.Overlay checked name="testBaseLayer">
           <GeoJSON data={ this.state.geoJson } />
        </LayersControl.Overlay>
     </LayersControl>
 </Map>

And this is the error I'm getting:

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.

I've checked and the error only happens for the LayersControl.BaseLayer and Overlay. Are these not supported or am I doing something incorrectly?

Thanks in advance for your help.

Get bounds of the map on moveend event

I have a question, How can I get bounds when map moves?
These are my codes.

onMapMoveEnd(e) {
     console.log('new bounds ', this.map.leafletElement);
}  

render() {
      const center = this.getCenter();
      const zoom = this.getZoomLevel();
      const bounds = this.getBounds();
      const boundsOptions = this.getBoundsOptions();
      return (
        <Map
          center={center}
          zoom={zoom}
          bounds={bounds}
          boundsOptions={boundsOptions}
          zoomControl={false}
          onMoveend={this.onMapMoveEnd}
          ref={(component) => {
            this.map = component;
          }}
        >
          <TileLayer attribution={tileAttribute} url={tileSource} />
          {this.renderMapMarkersAndPolylines()}
        </Map>
      );
    }
}

Unable to create custom marker icon and pass it to Marker component

Here are my code files
customMarker.js

import L from 'leaflet';

export default customMarkerIcon = new L.Icon({
    iconUrl: require("./marker.png"), 
    iconAnchor: null,
    popupAnchor: null,
    shadowUrl: null,
    shadowSize: null,
    shadowAnchor: null,
    iconSize: [512,512],
    iconAnchor: [32, 64],
    className: 'leaflet-div-icon'
});

LeafletMap.js

import dynamic from "next/dynamic";
const customMarkerIcon = dynamic(() => import("./customMarker"), {
    ssr: false,
});

 <Marker
    icon={customMarkerIcon}
    key={_id}
    position={coordinates}
>
    <Popup>{address}</Popup>
</Marker>

It just doesn't render anything in this case and throws this error

    at NewClass._initIcon (leaflet-src.js:7593)
    at NewClass.onAdd (leaflet-src.js:7500)
    at NewClass._layerAdd (leaflet-src.js:6617)
    at NewClass.whenReady (leaflet-src.js:4477)
    at NewClass.addLayer (leaflet-src.js:6679)
    at NewClass.addLayer (leaflet-src.js:6830)
    at NewClass.addLayer (leaflet-src.js:6974)
    at NewClass._animationAddLayerNonAnimated (leaflet.markercluster-src.js:1123)
    at NewClass.addLayer (leaflet.markercluster-src.js:144)
    at Marker.componentDidMount (MapLayer.js:38)
    at commitLifeCycles (react-dom.development.js:19814)
    at commitLayoutEffects (react-dom.development.js:22803)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22541)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at scheduleUpdateOnFiber (react-dom.development.js:21199)
    at dispatchAction (react-dom.development.js:15660)
    at checkForUpdates (use-subscription.development.js:179)
    at loadable.js:226
    at Set.forEach (<anonymous>)
    at LoadableSubscription._update (loadable.js:226)
    at loadable.js:214```

various react-universal component examples are throwing errors using react-leaflet-universal in Razzle SSR app

 ive been trying to use react-leaflet-universal in a razzle.js SSR app and quite a lot of the examples from the react-leaflet repo are failing with a variety of errors. I have tested the same components with vanilla React CRA app and they work ok.

Components being tested:
https://github.com/PaulLeCam/react-leaflet/tree/master/example/components

Expected behavior

  1. All the example components from main repo should work using following package.json spec:
    "express": "^4.17.1",
    "leaflet": "^1.6.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-leaflet": "^2.6.1",
    "react-leaflet-universal": "^2.1.0",
    "react-router-dom": "^5.0.1"

Actual behavior

I'm systematically testing all the examples in my test app and im logging the components that don't immediately work with react-leaflet-universal but work fine with react-leaflet in a React SPA.

Following examples are broken showing errors below:

DRAG AND DROP
https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/draggable-marker.js

result:

TypeError: Cannot read property 'getLatLng' of undefined
NewClass.DraggableExample.updatePosition
src/Draggable.tsx:42
  39 |   const marker = this.refmarker.current
  40 |   if (marker != null) {
  41 |     this.setState({
> 42 |       marker: marker.leafletElement.getLatLng(),
     | ^  43 |     })
  44 |   }
  45 | }

EVENTS HANDLING
https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/events.js

result: clicking anywhere on the map results white screen and error:

Uncaught TypeError: Cannot read property 'locate' of undefined
    at NewClass.EventsExample.handleClick (Events.tsx:28)
    at NewClass.fire (leaflet-src.js:593)
    at NewClass._fireDOMEvent (leaflet-src.js:4452)
    at NewClass._handleDOMEvent (leaflet-src.js:4409)
    at HTMLDivElement.handler (leaflet-src.js:2679)

LAYERS CONTROL
https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/layers-control.js

SVG OVERLAY
https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/svg-overlay.js

both are giving similar error reports:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `BrokenExample`.
    in BrokenExample (created by Home)
    in div (created by Home)
    in Home (created by Context.Consumer)
    in Route (created by App)
    in Switch (created by App)
    in App
    in Router (created by BrowserRouter)
    in BrowserRouter
console.<computed> @ index.js:1
warningWithoutStack @ react.development.js:167
warning @ react.development.js:638
createElementWithValidation @ react.development.js:2035
render @ LayersControl.tsx:31
finishClassComponent @ react-dom.development.js:18470
updateClassComponent @ react-dom.development.js:18423
beginWork$1 @ react-dom.development.js:20186
beginWork$$1 @ react-dom.development.js:25756
performUnitOfWork @ react-dom.development.js:24695
workLoopSync @ react-dom.development.js:24671
performSyncWorkOnRoot @ react-dom.development.js:24270
scheduleUpdateOnFiber @ react-dom.development.js:23698
updateContainer @ react-dom.development.js:27103
(anonymous) @ react-dom.development.js:27528
unbatchedUpdates @ react-dom.development.js:24433
legacyRenderSubtreeIntoContainer @ react-dom.development.js:27527
hydrate @ react-dom.development.js:27591
./src/client.tsx @ client.tsx:4
__webpack_require__ @ bootstrap:725
fn @ bootstrap:100
1 @ react.svg:1
__webpack_require__ @ bootstrap:725
(anonymous) @ bootstrap:792
(anonymous) @ bootstrap:792
index.js:1 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

WSM TILE LAYER
https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/wms-tile-layer.js#L27

result:

GET https://demo.boundlessgeo.com/geoserver/ows?service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&children=undefined&width=256&height=256&srs=EPSG%3A3857&bbox=0,7514065.628545967,1252344.2714243277,8766409.899970293 net::ERR_CONNECTION_TIMED_OUT

Is there a repo of elements tested and confirmed working with "react-leaflet-universal"?

thanks

[ error ] ./node_modules/react-leaflet-universal/dist/decorator.js

Please test react-leaflet-universal with the new version of next.js

"next": "9.3.4"

There is an error during compilation

[ error ] ./node_modules/react-leaflet-universal/dist/decorator.js
Module not found: Can't resolve 'babel-runtime/core-js/object/get-prototype-of' in '/Users/gromchen/Desktop/gitlab/zakaz-stores-web/node_modules/react-leaflet-universal/dist'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.