score:0

Try to create a function in place where your Popup controller is, which dispatches something and pass it to that component. I mean something like:

const doSomething = () => {
  dispatch(do());
};

const renderedContent = renderToString(
  <Popup
    port={port}
    poiType={poiType}
    activeView={activeView}
    isPortsOnly={isPortsOnly}
    locations={locations}
    onDoSomething={doSomething}
  />
)

score:3

Well this was interesting! I've made a sandbox with the following code, scroll for some explanations:

Popup.js

import React, { useRef, useEffect } from "react";
import { connect } from "react-redux";
import { Popup } from "mapbox-gl";

import { zoomAction } from "./ActionCreators";

const CustomPopup = ({ zoomAction, lng, lat, zoom, map }) => {
  const popUpRef = useRef(null);

  useEffect(() => {
    if (popUpRef.current && map) new Popup().setLngLat([lng, lat]).setDOMContent(popUpRef.current).addTo(map);
  }, [popUpRef, map, lng, lat]);

  const zoomIn = () => {
    zoomAction();
    map.zoomIn(zoom);
  };

  return (
    <div ref={popUpRef}>
      <h1>Hello World!</h1>
      <button onClick={zoomIn}>zoom in (currently {zoom})</button>
    </div>
  );
};

const mapStateToProps = (state) => {
  return { ...state };
};

const mapDispatchToProps = { zoomAction };

export default connect(mapStateToProps, mapDispatchToProps)(CustomPopup);

MapGl.js

import React, { useMemo, useState } from "react";
import MapboxGl from "mapbox-gl";
import { connect } from "react-redux";

import "mapbox-gl/dist/mapbox-gl.css";
import "./MapGl.css";

import Popup from "./Popup";

MapboxGl.accessToken = "pk.eyJ1IjoibmljZXk3MjMxMSIsImEiOiJja2hrdGE4MHkwMGxxMndteDRucGIyMDVqIn0.MKokCfCrE8VskLRUNGO0hw";

const MapGl = ({ zoom, lng, lat }) => {
  const [isLoaded, setIsLoaded] = useState(false);
  const map = useMemo(() => {
    if (isLoaded)
      return new MapboxGl.Map({
        container: document.querySelector(".mapContainer"),
        style: "mapbox://styles/mapbox/streets-v11",
        center: [lng, lat],
        zoom: zoom
      });
  }, [isLoaded]);

  return (
    <div>
      <div ref={() => setIsLoaded(true)} className="mapContainer" />
      {map && <Popup map={map} />}
    </div>
  );
};

const mapStateToProps = (state) => {
  return { ...state };
};

export default connect(mapStateToProps)(MapGl);

MapGl.css

.mapContainer {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
}

App.js

import React from "react";

import "./App.css";

import MapGl from "./MapGl";

export default () => {
  return <MapGl />;
};

Index.js

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";

import "./index.css";

import store from "./Store";
import App from "./App";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

Store.js

import { createStore } from "redux";

const initialState = {
  lng: -96,
  lat: 37.8,
  zoom: 3
};

function rootReducer(state = initialState, action) {
  switch (action.type) {
    case "zoom":
      return { ...state, zoom: state.zoom + 1 };
    default:
      return state;
  }
}

let store = createStore(rootReducer);

export default store;

ActionCreators.js

export const zoomAction = () => ({
  type: "zoom"
});

First thing is to replace the setHtml with setDOMContent which takes an existing DOM node instead of a string. Secondly the use of renderToString is tied to server-side rendering which is situational. In this example I make a popup with a button that dispatches a zoom action to update the store as well as syncing the map object itself. You can see the new zoom value applied to the map and the store value updated inside the popup.


Related Query

More Query from same tag