Managing multiple React roots using MutationObserver

Amir Hachaichi
Jun 14 · 2 min read

If you want to add React to an existing web application, you will most likely need to use ReactDOM.render( ) more than one time. If it’s on the same screen, it should not be a problem, but if HTML elements come and go as you switch views, you will need to handle them separately, using functions defined in the index.js file.

According to React documentation, it’s perfectly fine to use multiple roots to render component trees, and in fact, it’s the way Facebook works with it.

In this example, I want to call a function defined in the index.js file when a button is clicked in a component defined in another file. I will be using data attributes, React useEffect hook, and MutationObserver.

Data attributes

First I want to store the name of the target screen in a DOM Element, so that I can access it from anywhere in the app. Data attributes, which starts with data-* are well-suited for this purpose, so the following tag will be added in the html file:

<input type="hidden" id="nextscreen" data-screen="nochange" />

Now I can access and modify the “attribute variable” screen using the dataset property:

document.getElementById("nextscreen").dataset.screen

React useEffect hook

The data attribute needs to be modified if a navigation button is clicked. Since the data containing element has not been created using React, the useEffect hook needs to be used, because we are making changes “outside” of React.

We need to initiate a variable with useState in order to be able to use it in useEffect: it is newscreen.

function navButton(props){  const [ newscreen, setScreen ] = useState("nochange");  const doSwitch = ()=>{    setScreen(props.target);  }  useEffect(()=>{    document.getElementById('nxtscreen').dataset.screen = newscreen;    console.log("Target screen is: "+newscreen);  },[newscreen]);  return (    <li className="nav-item">      <span className="nav-link" onClick={doSwitch}>{props.btxt}            </span>    </li>  );}

The value “nochange” will be assigned on first render, and if the button is clicked, the function doSwitch( ) will be invoked, assigning the value of props.target to newscreen, and writing it to the hidden input element.

MutationObserver

Finally I need some code to be executed when the data attribute gets updated.

MutationObserver allows listening to changes in a target element without causing performance issues, also it is supported by most browsers.

When the observer detects an attributes change in our target element, the callback function navSwitch( ) is invoked and the desired code can now be executed.

let observer = new MutationObserver(navSwitch);function navSwitch(mutations){  console.log(mutations);  // code to switch screen}observer.observe( document.getElementById('nxtscreen'), { attributes: true } );

I hope this was helpful, let me know if you have a better idea to achieve the same result!

Geek Culture

Proud to geek out. Follow to join our 1M monthly readers.