Search code examples
reactjsopenlayers

Open Layer Renders Map component twice in react


I'm using Open Layer to render map functionality in a react application. I used the simple code snippets to display the map in the application. Unfortunately the application renders the map twice as below.

enter image description here

Here is the code snippet I used to display the map:

export default function MapView() {
  const view = new View({
    center: [0, 0],
    zoom: 2,
  });
  useEffect(() => {
      new Map({
        layers: [
            new TileLayer({source: new OSM()}),
        ],
        view: view,
        target:'map',
      })
  },[])

  return (
    <>
      <div id="map" style={{width:'100%',height:'400px'}}></div>
    </>   
  )
}

And the parent component is as below

function App() {
  return (
    <div>
      <MapView />
    </div>
  );
}

Solution

  • Your component may be mounted multiple times; useEffect with an empty deps array runs each time the component is mounted, but instantiating View/Map appears to mutate the DOM directly and inject the map. If you look at my example, the message "I'm mounting!" is printed to the console twice. Because useEffect is running once per mount, you're instantiating multiple Map objects and attaching them to the element with id map. Instead, you should consider using a ref to hold your Map reference so that only one map object is instantiated per instance of your component.

    Additionally, rather than referencing an ID by string in your map mounting, you should instead use a ref for the component's rendered node, instead, and pass that reference to target (which does appear to accept an HTMLElement node directly).

    See this sandbox link for an example.

    This ref handle also gets you a persistent reference to the map object, which you can then use to perform imperative operations on the map in response to prop changes (see my example zoom-sensitive useEffect).