Geek Culture
Published in

Geek Culture

Managing multiple React roots using MutationObserver

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!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Amir Hachaichi

Amir Hachaichi

I write about web applications and javascript. I try to make tutorials that everyone can benefit from, regardless of the background