Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to render react component within mapboxgl.Popup() in .setHTML or .setDOMContent?

I am wondering if it is possible to render a react component within a mapboxgl.Popup(). Something like this:

    componentDidMount() {
            new mapboxgl.Popup()
            .setLngLat(coordinates)
            .setHTML(`<div>${<MapPopup />}<p>${moreText}</p></div>`)
            //.setDOMContent(`${<MapPopup />}`) ?????
            .addTo(this.props.mapboxMap)
    })

Or should this be done using ReactDOM.render?

ReactDOM.render(<MapPopup />, document.getElementById('root'))

This project will have buttons and inputs in the popup that connect to a redux store.

Thanks for any input!

like image 707
jasonrberney Avatar asked Feb 21 '18 22:02

jasonrberney


3 Answers

This works:

addPopup(el: JSX.Element, lat: number, lng: number) {
    const placeholder = document.createElement('div');
    ReactDOM.render(el, placeholder);

    const marker = new MapboxGl.Popup()
                        .setDOMContent(placeholder)
                        .setLngLat({lng: lng, lat: lat})
                        .addTo(map);
}

(Where I've used typescript to illustrate types, but you can just leave these out for pure js.) Use it as

addPopup(<h1>Losers of 1966 World Cup</h1>, 52.5, 13.4);
like image 89
thund Avatar answered Oct 12 '22 10:10

thund


You can try to implement React component:

export const Popup = ({ children, latitude, longitude, ...mapboxPopupProps }) => {
    // this is a mapbox map instance, you can pass it via props
    const { map } = useContext(MapboxContext);
    const popupRef = useRef();

    useEffect(() => {
        const popup = new MapboxPopup(mapboxPopupProps)
            .setLngLat([longitude, latitude])
            .setDOMContent(popupRef.current)
            .addTo(map);

        return popup.remove;
    }, [children, mapboxPopupProps, longitude, latitude]);

    return (
        /**
         * This component has to have 2 divs.
         * Because if you remove outter div, React has some difficulties
         * with unmounting this component.
         * Also `display: none` is solving that map does not jump when hovering
         * ¯\_(ツ)_/¯
         */
        <div style={{ display: 'none' }}>
            <div ref={popupRef}>
                {children}
            </div>
        </div>
    );
};

After some testing, I have realized that Popup component was not rendering properly on the map. And also unmounting the component was unsuccessful. That is why there are two divs in return. However, it may happen only in my environment.

See https://docs.mapbox.com/mapbox-gl-js/api/#popup for additional mapboxPopupProps

useEffect dependencies make sure that MapboxPopup gets re-created every time something of that list changes & cleaning up the previous popup instance with return popup.remove;

like image 42
Jan Dočkal Avatar answered Oct 12 '22 12:10

Jan Dočkal


I've been battling with this as well. One solution I found was using ReactDOM.render(). I created an empty popup then use the container generated by mapboxgl to render my React component.

    marker.setPopup(new mapboxgl.Popup({ offset: 18 }).setHTML(''));


     markerEl.addEventListener('mouseenter', () => {
        markerEl.classList.add('enlarge');

        if (!marker.getPopup().isOpen()) {
          marker.getPopup().addTo(this.getMap());

          ReactDOM.render(
            component,
            document.querySelector('.mapboxgl-popup-content')
          );
        }
      });
like image 25
dunncl15 Avatar answered Oct 12 '22 10:10

dunncl15