1

I'm trying to connect a leaflet.js marker popup content to a redux store inside a react component - I want the marker to update directly from the store every time it's data changes.

Each marker represents a station and when you click on the station it gives you an estimate from the store but the problem comes when you leave the popup open -it will not update - because it's not 'connected' to the store - the data is being loaded by the react component from the store when they click on the component - a callback.

Maybe my entire methodology of work is incorrect, any suggestions are welcome!

this is the Station code:

import L from "leaflet";
import { Component } from "react";
class Station extends L.Marker {
  constructor(stationData, clickCallback, creatorObj) {
    super(new L.LatLng(stationData.gtfs_latitude, stationData.gtfs_longitude), {
      icon: L.divIcon({
        className: "station-icon",
        iconSize: [9, 9]
      })
    });
    this.popup = L.popup().setContent(
      "Station: <b>" + stationData.name + "</b>"
    );
    this.bindPopup(this.popup);
    this.data = stationData;
    if (creatorObj instanceof Component) {
      this.creatorObj = creatorObj;
    }
    if (clickCallback instanceof Function) {
      this.on("click", clickCallback);
    }
  }
}
export default Station;

this is where I load the stations static data from the store create the stationsLayers on the map:

  loadStations() {
    const { stations, dispatch } = this.props;

    //Creating a new list of station layers to send to the store
    let stationsLayers = [];

    //Creating a ref for the current object to access state in functions of the station layer
    const trainMapObj = this;
    //Creating a ref for station click event handler
    const stationOnClickRef = this.stationOnClick;
    stations.items.map(function(station) {
      //Creating a new station layer object and passing it the station data, a function for onclick event callback and a ref
      //to the current object to access current app state (can't connect because its not a component)
      const stationLayer = new Station(station, stationOnClickRef, trainMapObj);
      stationsLayers.push(stationLayer);
    });
    //Dispatching the layer list created to the store for the map update
    dispatch(addMapLayers(stationsLayers));

    //Letting the state know that the station load already has been done
    this.state.stationsReady = true;
  }

this is the callback for the station layer click (each station is a layer):

stationOnClick(event) {
    //Getting the station layer that called this event handler
    const stationLayer = event.target;

    //Getting the estimates from the state for this station (by station abbr)
    const estimatesOfStation = _.find(
      stationLayer.creatorObj.props.estimates.estimates,
      ["abbr", stationLayer.data.abbr]
    );
    //Checking if there is any estimates for the station
    if (estimatesOfStation !== undefined) {
      //OLD CODE: DIDN'T CHANGE
      let platforms = [];
      estimatesOfStation.etd.map(function(destination) {
        destination.estimate.map(function(estimate) {
          var plat = estimate.platform;
          if (!platforms[plat]) {
            platforms[plat] = {
              dir: estimate.direction,
              trains: []
            };
          }

          platforms[plat].trains.push({
            mins: estimate.minutes,
            destId: destination.abbreviation,
            dest: destination.destination,
            color: estimate.color,
            hexColor: estimate.hexcolor,
            bikeFlag: estimate.bikeflag == "1"
          });
        });
      });
      var stationInfo = "Station: <b>" + estimatesOfStation.name + "</b>";
      platforms.map(function(platform, platId) {
        platform.trains.sort((a, b) => toInt(a.mins) - toInt(b.mins));
        stationInfo += "<br>Platform " + platId + ": " + platform.dir;
        platform.trains.forEach(function(train) {
          let bikeSupport = "";
          if (train.bikeFlag) {
            bikeSupport =
              "<img style='height:15px; vertical-align:center' src='" +
              bike +
              "' />";
          }
          stationInfo +=
            "<br> <img class='bart-bullet' style='background:" +
            train.hexColor +
            "'/>" +
            train.mins +
            " min -- " +
            train.dest +
            " (" +
            bikeSupport +
            ", " +
            train.color.toLowerCase() +
            ")";
        });
        stationInfo += "<br>";
      });

      //Set the new content of the station popup
      stationLayer.setPopupContent(stationInfo);
    }
  }
3
  • 1
    1) Is it possible for you to link to a minimal reproducible example of the problem with the source code publicly available? In my experience questions involving redux are often pretty difficult to debug without this. 2) Is there a reason why you aren't using react-leaflet? 3) Not to self plug here but I wrote a train map that does very similar things to what you are trying to do here. Maybe looking at this code will help you figure out a better solution? github live site
    – jbccollins
    Commented Jul 10, 2019 at 20:55
  • 1 - yes, only code for now - github.com/avivbueno/bartwatch/tree/feat/add_stations_to_map , I will upload a live example tomorrow morning. 2 - I probably need to learn it's documentation better. Had few problems with it. 3 - thanks I think it will be pretty helpful.
    – Avivbuen
    Commented Jul 10, 2019 at 22:03
  • 1
    In current perspective my question was way too specific to the case: the thing I needed is a way to subscribe to only parts of the store. FOUND A SOLUTION in the documentation of redux (at the bottom of the page - links to libs that do that - I used redux-subscriber) redux.js.org/faq/…
    – Avivbuen
    Commented Jul 13, 2019 at 16:14

0

Browse other questions tagged or ask your own question.