Skip to content

Disable component for SSR #35

@ptondereau

Description

@ptondereau

I'm in situation where I need to render to null this component when window global is not present (because of SSR). I use this lib to avoid error in SSR node server --> https://github.com/masotime/react-leaflet-universal

I've forked your repo to change LayerGroup import origin and added same logic of render present in react-leaflet-universal but I don't really understand the API. Indeed, an error is throw about this.container.map is undefined so I can't access to the container.

Any clue for how to achieve this?

Here is my snippet:

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: {}
};

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions