Mounting React components in a different framework
Tags: react, backbone
Published: 2019-06-26 19:38:53

The project we are working on started as a [Backbone.js](https://backbonejs.org/) project, but we now began to integrate React into it.
This post is not about the reasoning behind that, but about something different:
how we use (or mount) React components inside a Backbone application.

When we write a new React app from scratch, we define our entrypoint component, usually called `App`, and mount it somewhere via ReactDOM into the existing DOM:
`ReactDOM.render(<App />, document.getElementById("root"));`.
We will then start to develop the application, which completely resides in that `App` component.

But this is not the case when we have an existing application written with another framework (in our case backbone), that we now want to use React inside it.
Our choices were to either:
1. Rewrite the whole application from scratch
2. Realize new features with React, and slowly replace Backbone.js code by React code in the process

For many reasons (which might be discussed in a future post), we chose option 2.

----

Lets define a new component that we want to integrate in our existing application:
```ts
function CounterButton() {
  // Define state using hooks
  const [count, setCount] = React.useState<number>(0);

  // Return button displaying current state and incrementing state on click
  return (
    <button onClick={
      () => setCount(count + 1)
    }>
      {count}
    </button>
  )
}

```

The `CounterButton` component renders a button that shows how often the user has clicked on it.
This component has a state `count`, initially set to `0`, and the corresponding setter function `setCount`.

Now, in order to add `CounterButton` to our existing application at some place, we use `ReactDOM.render` to render it into an existing DOM element:
`ReactDOM.render(<CounterButton />, document.getElementById("someElement"));`.

And we are done!

---------

*Or so we thought.*

What if you want to reuse the same component at the same place at a later time?
For example a modal (also known as dialogue), that the user closes at some point but might eventually open up again.

Let's add a `show` state to the `CounterButton` component, which can make the `<button>` disappear:
```js
function CounterButton() {
  // Define state using hooks
  const [count, setCount] = React.useState(0);
  const [show, setShow] = React.useState(true);

  // Return button displaying current state and incrementing state on click
  if (!show) {
    return null;
  }
  return (
    <button onClick={
      () => {
        if (count === 5) {
          setShow(false);
        }
        setCount(count + 1);
      }
    }>
      {count}
    </button>
  )
}
```
`CounterButton` will now return `null` if `!show` yields true, completely removing `<button>` from the DOM when that `show` state changes from `true` to `false`.
Here, this is the case when `count` is `5` at the time the user clicks the button.

This logic is what we currently use to close a modal.
When the user triggers the close logic of that modal, we set the `show` state to `false` which result in the modal being removed from the DOM..

But what if you want to show `CounterButton` again after it disappeared?
Simply execute the following call again, right?
`ReactDOM.render(<CounterButton />, document.getElementById("someElement"));`
Sadly, `CounterButton` will not show up.

From the [React docs](https://reactjs.org/docs/react-dom.html#render):
> If the React element was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.

In other words, ReactDOM will render the *same instance* as before, only with updated props.
React will use the instance of `CounterButton`, that was previously used, with the same state: `show` is still `false`.

Our first idea to solve this issue was to create a new instance of `CounterButton` every time before we pass it to `ReactDOM.render`.
For this, we encapsulated the body of the `CounterButton` function inside an arrow function, essentially an anonymous functional component. `CounterButton` will now return this anonymous functional component:
```js
function CounterButton() {
  return () => {
    // Define state using hooks
    const [count, setCount] = React.useState(0);
    const [show, setShow] = React.useState(true);

    // Return button displaying current state and incrementing state on click
    if (!show) {
      return null;
    }
    return (
      <button onClick={
        () => {
          if (count === 5) {
            setShow(false);
          }
          setCount(count + 1);
        }
      }>
        {count}
      </button>
    )
  }
}

// Create new functional component to pass into ReactDOM.render
const CounterButtonInstance = CounterButton();
ReactDOM.render(<CounterButtonInstance  />, document.getElementById("root"));
```
No matter how often we call `ReactDOM.render` with a return of `CounterButton()` into `document.getElementById("root")`, `ReactDOM.render` will always see this anonymous functional component as different component as the one before.
That is because it *is* a different anonymous functional component.

But this approach has at least one issue:
`CounterButton` is not a functional component anymore, but a function returning a functional component.
This makes reusing `CounterButton` inside a React application impossible.

Now, for our current solution, we removed that encapsulation introduced in the last code snippet.
Instead, we make use of the special component prop `key`, read more about it the [React docs](https://reactjs.org/docs/lists-and-keys.html):
```ts
ReactDOM.render(
  <CounterButton key={new Date().getTime()} />, document.getElementById("root")
);
```
We make use of an important attribute of the `key` prop here: if React is about to re-render a component which has its `key` *changed* since the last render, React will discard that previous version and render it from scratch. 
We use the current time (in milliseconds) as value for that prop; and since this will change between renders, React will create a new instance of `CounterButton` with a fresh state! 🎉

Below you see a codepen showcasing this approach.
Click that button a few times, and it will disappear to never come back again.
But if you uncomment those key props, `CounterButton` will get reset every 2 seconds.

{% codepen https://codepen.io/eljenso/pen/WqZzQq default-tab=js,result %}


-----------

*Some afterthoughts*

For that anonymous functional component, we could also had introduced another function that returns an anonymous functional returning the original `CounterButton`:
```js
function CreateCounterButton() {
  return () => CounterButton()
}

```
Calling `CreateCounterButton` will then create a new instance of `CounterButton` on every call.
This will keep our `CounterButton` reusable.

Any of the approaches described above have a drawback:
`CounterButton` will still be part of the ReactDOM, even after its removed from the DOM.
We should make sure that `CounterButton` is properly unmounted from the ReactDOM once it is not used anymore; otherwise, it can be considered a memory leak, which can result in performance issues.
ReactDOM provides an `unmountComponentAtNode(container)` method, which allows to unmount any React component mounted in the `container`.

In our example, we would utilize it like this:
```js
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
```
But since `CounterButton` is not, and should not be, aware that it has to be unmounted this way, that call should be handled from the outside.

We did not look further into using `unmountComponentAtNode` yet.
Since we do not have many React components yet (we currently have around 40 tsx files in the codebase), the `key` prop approach seems sufficient.
We should look further into this approach once the think that leaving unused components in the ReactDOM affects the performance of our application.