1

I have a class inheriting React.Component as such :

import { Component } from "react"

import {Map} from "ol" // the goal is to render a map with openlayers

import './MapCanvas.css'

class MapCanvas extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }

    componentDidMount() {
        console.log('hello') // called twice

        const canvas = new Map(/* map options, irrelevant here */)
        
        this.setState({canvas})
    }

    
    componentWillUnMount () {
        console.log('goodbye') // never called

        if (this.state && this.state.canvas) {
            this.state.canvas.setTarget(undefined) // should remove the map
        }
    }
    

    render() {
        return (
            <div id="map" className="map"></div>
        )
    }
}

export default MapCanvas

My app looks like this

import MapCanvas from './components/MapCanvas'

import './App.css'

function App() {
    return (
        <MapCanvas/>
    )
}

export default App

and index is

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import App from './views/App';

const root = createRoot(document.getElementById('app'))

root.render(
    <StrictMode>
        <App/>
    </StrictMode>
)

If I remove StrictMode, Everything looks fine, However if I leave it, the componentDidMount method of MapCanvas gets called twice, and I end up having 2 maps on the page. componentWillUnmount never gets called in the process.

I read here and there that React StrictMode calls some functions multiple times in Developpement mode.

As far as I understood, it is to help us code in a cleaner way, but I can't understand what I am supposed to do.

My question is, where am I supposed to setup the ol.Map so that it respects the best practices and gets called only once, OR gets properly "destroyed" on update ?


edit

just as posting the question I realized I had a typo on componentWillUnmount (M instead of m) I Corrected it, now I see "goodbye" in the console, but I still have 2 maps. If this question is more about openlayer thant about react, let it know in the comments and I'll update or delete it


edit #2

Using class attributes instead of React state to store the canvas gives the expected result. Is this considered good practice or is there a better way ?

gui3
  • 1,711
  • 14
  • 30
  • Check this [answer](https://stackoverflow.com/a/72256024/13405106), might help you – Usama May 17 '22 at 12:32
  • `componentWillUnmount` should contain logic to clean up whatever was created in `componentDidMount`. If that doesn't work then you need to investigate that part. – Felix Kling May 17 '22 at 12:32
  • Thanks for the quick responses, @Usama you mean I shouldn't use canvas.setTarget to avoid state mutations ? Where am I supposed to store the canvas If I want to mutate it ? as a class attribute ? I don't find clear answers. @"Felix Kling" I'm investigating openlayers right now and I'll delete the question if my problem comes from here – gui3 May 17 '22 at 12:40
  • 1
    Don't know much as I am also learning about this but this [answer](https://stackoverflow.com/a/40309023/13405106) may help – Usama May 17 '22 at 12:55

1 Answers1

0

Here is a workaround that worked for me: using a class attribute instead of the React State

import { Component, createRef } from "react"

import { Map } from "ol"

class MapCanvas extends Component {
    constructor(props) {
        super(props)
        this.canvas = new Map(/* options */)
        this.ref = createRef()
    }

    componentDidMount() {
        this.canvas.setTarget(this.ref.current)
    }

    
    componentWillUnmount () {
        this.canvas.setTarget(undefined)
    }
    

    render() {
        return (
            <div ref={this.ref} className="map"></div>
        )
    }
}

export default MapCanvas

It may not be good react practice, but it solves the prolem for now

If you have a better solution you can post an answer i'll consider accepting it instead

gui3
  • 1,711
  • 14
  • 30