Search code examples
reactjseventsstylingreact-leaflet

How to conditionally style CircleMarkers in React Leaflet?


So, like some other users, I've recently bumped into the problem of adding React functionality to popups on the React Leaflet GeoJSON Layer. After reading several posts here on SO, the issue apparently comes down to a mismatch between using native Leaflet features and the features abstracted out with the React Leaflet implementation. In using the native Leaflet methods, the popup binding only accepts a string, which prevents adding JSX or additional interaction.

In abandoning the GeoJSON component, I'm also losing the onEachFeature function which is the main way to easily style and add events to the layer. In refactoring my app to use Circle markers, I can't seem to figure out how to conditionally style the individual markers based on the feature's properties. It seems that the GeoJSON Layer pathOptions prop doesn't seem to accept a function like so:

import { CircleMarker, Popup } from "react-leaflet";
import PopupContent from "./PopupContent";

const CircleMarkerLayer = ({ data }) => {
    return  data.features.map((feature) => {
    const { coordinates } = feature.geometry
    const markerStyles = function(feature) {
        switch (feature.properties.type) {
            case 'Sticker': return {color: '#a50026'};
            case 'Mural':   return {color: '#d73027'};
            case 'Marker':   return {color: '#f46d43'};
            case 'Characters':   return {color: '#fdae61'};
            case 'Letters':   return {color: '#fee090' };
            case 'Tippex':   return {color: '#ffffbf'};
            case 'Spray':    return {color: '#e0f3f8'};
            case 'Chalk':    return{color: '#abd9e9'};
            case 'Label maker sticker':    return{color: '#74add1' };
            case 'Poster':    return{color: '#4575b4'};
            }
      }
    return (
        <CircleMarker key={feature.properties.id} center={[coordinates[1], coordinates[0]]} pathOptions={markerStyles}>
            <Popup>
                <PopupContent content={feature} />
            </Popup>
        </CircleMarker>
    )
    }

    )
}

Additionally, it's not clear how to add additional events such as MouseOver and MouseOut, which I want to use as part of my UI. My ultimate goal was to add a LightBox to the images in my popups, but for now I'm stumped with conditional styling.


Solution

  • The CircleMarker can still take a pathOptions which should be of the type:

    export interface PathOptions extends InteractiveLayerOptions {
        stroke?: boolean | undefined;
        color?: string | undefined;
        weight?: number | undefined;
        opacity?: number | undefined;
        lineCap?: LineCapShape | undefined;
        lineJoin?: LineJoinShape | undefined;
        dashArray?: string | number[] | undefined;
        dashOffset?: string | undefined;
        fill?: boolean | undefined;
        fillColor?: string | undefined;
        fillOpacity?: number | undefined;
        fillRule?: FillRule | undefined;
        renderer?: Renderer | undefined;
        className?: string | undefined;
    }
    

    So in this case just use the function that you already have:

    import { CircleMarker, Popup } from "react-leaflet";
    import PopupContent from "./PopupContent";
    
    const markerStyles = (feature) => {
      switch (feature.properties.type) {
        case "Sticker":
          return { color: "#a50026" };
        case "Mural":
          return { color: "#d73027" };
        case "Marker":
          return { color: "#f46d43" };
        case "Characters":
          return { color: "#fdae61" };
        case "Letters":
          return { color: "#fee090" };
        case "Tippex":
          return { color: "#ffffbf" };
        case "Spray":
          return { color: "#e0f3f8" };
        case "Chalk":
          return { color: "#abd9e9" };
        case "Label maker sticker":
          return { color: "#74add1" };
        case "Poster":
          return { color: "#4575b4" };
      }
    };
    const CircleMarkerLayer = ({ data }) => {
      return data.features.map((feature) => {
        const { coordinates } = feature.geometry;
        return (
          <CircleMarker
            key={feature.properties.id}
            center={[coordinates[1], coordinates[0]]}
            pathOptions={markerStyles(feature)}
          >
            <Popup>
              <PopupContent content={feature} />
            </Popup>
          </CircleMarker>
        );
      });
    };