Popup always open in the marker
Asked Answered
B

7

7

Is there any way the popup always stays open and does not need to click on it to open?

Brassware answered 2/8, 2016 at 20:47 Comment(4)
Can you add an example of what you already have? More specifically what kind of data, and what components you are using.Orva
When you say "stay open", do you mean that when the map loads the popup is already open?Postulate
Also, you posted the same screenshot for both links :PPostulate
Yes @EricPalakovichCarr. Thks, i changed the image lolBrassware
S
13

What you can do is to make your own Marker class from the react-leaflet marker, and then call the leaflet function openPopup() on the leaflet object after it has been mounted.

// Create your own class, extending from the Marker class.
class ExtendedMarker extends Marker {
    componentDidMount() {
        // Call the Marker class componentDidMount (to make sure everything behaves as normal)
        super.componentDidMount();

       // Access the marker element and open the popup.
      this.leafletElement.openPopup();
    }
}

This will make the popup open once the component has been mounted, and will also behave like a normal popup afterwards, ie. on close/open.

I threw together this fiddle that shows the same code together with the basic example.

Schadenfreude answered 16/8, 2016 at 9:31 Comment(2)
nice one. in case one is willing to keep the popup open after the marker has been moved, following code has to be added: componentDidUpdate(prevProps, prevState) { super.componentDidUpdate(prevProps, prevState); this.leafletElement.openPopup(); }Barnaba
Hi, I did as you suggested but I get en error: TypeError: Marker is not a constructor. Why?Phalan
A
20

With the introduction of react-leaflet version 2 which brings breaking changes in regard of creating custom components, it is no longer supported to extend components via inheritance (refer this thread for a more details)

In fact React official documentation also recommends to use composition instead of inheritance:

At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.

Props and composition give you all the flexibility you need to customize a component’s look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions.

The following example demonstrates how to extend marker component in order to keep popup open once the marker is displayed:

const MyMarker = props => {

  const initMarker = ref => {
    if (ref) {
      ref.leafletElement.openPopup()
    }
  }

  return <Marker ref={initMarker} {...props}/>
}

Explanation:

get access to native leaflet marker object (leafletElement) and open popup via Marker.openPopup method

Here is a demo

Abbatial answered 3/2, 2019 at 21:51 Comment(1)
can be shorten to const initMarker = ref => { ref?.leafletElement.openPopup() }. but nice solution since it is working with functional componentsMegagamete
S
13

What you can do is to make your own Marker class from the react-leaflet marker, and then call the leaflet function openPopup() on the leaflet object after it has been mounted.

// Create your own class, extending from the Marker class.
class ExtendedMarker extends Marker {
    componentDidMount() {
        // Call the Marker class componentDidMount (to make sure everything behaves as normal)
        super.componentDidMount();

       // Access the marker element and open the popup.
      this.leafletElement.openPopup();
    }
}

This will make the popup open once the component has been mounted, and will also behave like a normal popup afterwards, ie. on close/open.

I threw together this fiddle that shows the same code together with the basic example.

Schadenfreude answered 16/8, 2016 at 9:31 Comment(2)
nice one. in case one is willing to keep the popup open after the marker has been moved, following code has to be added: componentDidUpdate(prevProps, prevState) { super.componentDidUpdate(prevProps, prevState); this.leafletElement.openPopup(); }Barnaba
Hi, I did as you suggested but I get en error: TypeError: Marker is not a constructor. Why?Phalan
S
4

You can use permanent tooltips, or React provides refs for this type of thing... you can do this:

https://jsfiddle.net/jrcoq72t/121/

const React = window.React
const { Map, TileLayer, Marker, Popup } = window.ReactLeaflet

class SimpleExample extends React.Component {
  constructor () {
    super()
    this.state = {
      lat: 51.505,
      lng: -0.09,
      zoom: 13
    }
  }

  openPopup (marker) {
    if (marker && marker.leafletElement) {
      window.setTimeout(() => {
        marker.leafletElement.openPopup()
      })
    }
  }

  render () {
    const position = [this.state.lat, this.state.lng]
    return (
      <Map center={position} zoom={this.state.zoom}>
        <TileLayer attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" />
        <Marker position={position} ref={this.openPopup}>
          <Popup>
            <span>
              A pretty CSS3 popup. <br /> Easily customizable.
            </span>
          </Popup>
        </Marker>
      </Map>
    )
  }
}

window.ReactDOM.render(<SimpleExample />, document.getElementById('container'))

References:

https://reactjs.org/docs/refs-and-the-dom.html

React.js - access to component methods

Auto open markers popup on react-leaflet map

Straightaway answered 17/12, 2018 at 0:53 Comment(0)
C
4

The above no longer works with react-leaflet version 3. In your custom marker component, to get a reference to the leaflet element you should now use useRef() and then open up the popup in useEffect() once the component is mounted.

const MyMarker = (props) => {
  const leafletRef = useRef();
  useEffect(() => {
    leafletRef.current.openPopup();
  },[])
  return <Marker ref={leafletRef} {...props} />
}
Core answered 23/3, 2021 at 19:27 Comment(1)
This example lead to this kind of error, TypeError: Cannot read property 'openPopup' of undefined. Do you know how to fix this?Doubletongued
T
2

You can open the popup once the marker is added to the map:

import { Marker, Popup } from "react-leaflet";

function MyMarker() {
    const position = [41.0, 2.11];

    function openPopup(e) {
        e.target.openPopup();
    }

    return (
        <Marker position={position} eventHandlers={{ add: openPopup }}>
            <Popup closeButton={false} autoClose={false} closeOnClick={false}>
                {/* Content of your popup... */}
            </Popup>
        </Marker>
    )
}

This will additionally prevent the popup from closing. Works with react-leaflet 4.2.1.

Terrorize answered 22/3 at 11:45 Comment(0)
S
1

For the new react-leaflet v4 you will need to do some changes

   const CustomMarker = ({ isActive, data, map }) => {
                  const [refReady, setRefReady] = useState(false);
                  let popupRef = useRef();
                
                  useEffect(() => {
                    if (refReady && isActive) {
                      map.openPopup(popupRef);
                    }
                  }, [isActive, refReady, map]);
                
                  return (
                    <Marker position={data.position}>
                      <Popup
                        ref={(r) => {
                          popupRef = r;
                          setRefReady(true);
                        }}
                      >
                        {data.title}
                      </Popup>
                    </Marker>
                  );
                };

And then use MapContainer like this

const MapComponent = () => {
  const [map, setMap] = useState(null);
  return (
    <div>
      <MapContainer
        ref={setMap}
        center={[45.34416, 15.49005]}
        zoom={15}
        scrollWheelZoom={true}
      >
        <CustomMarker
          isActive
          map={map}
          data={{
            position: [45.34416, 15.49005],
            title: "Text displayed in popup",
          }}
        />
      </MapContainer>
    </div>
  );
};
Solace answered 28/5, 2022 at 11:13 Comment(1)
you should use the useMap hook provided by react-leaflet. That way you won't need to pass map as a propCaracas
C
0

Edited

Notice that my old solution would try to open the popup every time it renders. Found another solution that fit my needs to open it when the position changed. Notice that I look at position.lat, position.lng since it will think it always changes if you pass on the object.

And yes it is not perfect typescript but it is the best solution I could come up with.

const CustomMarker: React.FC<CustomMarkerProps> = ({ position, children }) => {
  const map = useMap();

  const markerRef = useRef(null);

  useEffect(() => {
    try {
      // @ts-ignore
      if (markerRef.current !== null && !markerRef.current.isPopupOpen()) {
        // @ts-ignore
        markerRef.current.openPopup();
      }
    } catch (error) {}
  }, [position.lat, position.lng]);

  return (
    <Marker ref={markerRef} position={position}>
      <Popup>{children}</Popup>
    </Marker>
  );
};

export default CustomMarker;

Old solution

Could not get it to work using useRef and useEffect. However, got it to work with calling openPopup() directly from the ref.

import { LatLngLiteral, Marker as LMarker } from "leaflet";
import React from "react";
import { Marker } from "react-leaflet";

export interface CustomMarkerProps {
  position: LatLngLiteral;
  open?: boolean;
}
const CustomMarker: React.FC<CustomMarkerProps> = ({
  position,
  open = false,
  children,
}) => {
  const initMarker = (ref: LMarker<any> | null) => {
    if (ref && open) {
      ref.openPopup();
    }
  };

  return (
    <Marker ref={initMarker} position={position}>
      {children}
    </Marker>
  );
};

export default CustomMarker;
Cheep answered 10/3, 2022 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.