Search code examples
reactjsreact-dom

Keep a reference of a DOM node when mounting / unmounting a React component in a non-React application


I'm using webpack to use react components in a non-react applications. The idea is to export these in a way so they can be mounted and dismounted on any DOM.

This is the html page where I'm testing the component:

<script type="text/javascript" src="<?php echo base_url('assets/javascripts/build/component.js') ?>"></script>
<button onclick="showComponent()" />show component</button>
<button onclick="hideComponent()" />hide component</button>
<div id='mainContainer'></div>

<script >
  function showComponent() {
    MyComponent.mount(document.getElementById('mainContainer'))
  }

  function hideComponent() {
    MyComponent.unmount()
  }
</script>

This is the webpack entry point:

import React from 'react'
import MyComponent from '/path/to/MyComponent'
import ComponentMounter from '/path/to/ComponentMounter'

let componentMounter = new ComponentMounter(<MyComponent />)

export let mount = componentMounter.mount
export let unmount = componentMounter.unmount

In the webpack config, I'm exporting the bundle as a library, using:

output: {
    path: './assets/javascripts/build/',
    filename: 'component.js',
    libraryTarget: 'var',
    library: 'MyComponent'
}

The ComponentMounter class:

import ReactDOM from 'react-dom'

class ComponentMounter {

  constructor(component) {
    this.component = component
  }

  mount = (element) => {
    // This renders the react 'component', mounting it in the DOM 'element'
    this.element = ReactDOM.render(this.component, element)
  }

  unmount = () => {
    // This unmounts the react 'component', removing the DOM 'element'
    ReactDOM.unmountComponentAtNode(this.element)
  }

}

export default ComponentMounter

Hopefully you can see what I'm trying to do there. When clicking on the show button, the component mounts without problems.

But when I try to unmount it, it seems the element is not a valid DOM element, as I get this warning:

invariant.js:38 Uncaught Invariant Violation: unmountComponentAtNode(...): Target container is not a DOM element.

How can I keep a reference to a React component mounted in a DOM node within ComponentMounter object so I can easily unmount it when needed?


Solution

  • You're setting this.element to be the result of ReactDOM.render which is incorrect. Simply change your mount function in ComponentMounter class to the following to solve your problem:

    mount = (element) => {
        this.element = element;
        ReactDOM.render(this.component, this.element);
    }
    

    This is because you want to call unmountComponentAtNode on the parent DOM node wheras previously you were setting this.element to the return value of ReactDOM.render which returns a reference to the component you mounted (or null) and NOT the dom node where the component is mounted.