⚛️ Reparenting is now possible with React
Tags: react, javascript, webdev
Published: 2020-05-23 17:00:04

_Originally published on [Medium](https://medium.com/@paolimi/reparenting-with-react-426d12fb6d0d)_

I am designing an app similar to [Trello](https://trello.com/). On the main page, I want some vertical Lists and some Cards that can be dragged from one List to another.

How can I transfer a Card component after dragging? With React it seems quite easy. To change the Parent component of a Child component, the components have to be **re-rendered** with that Child in its new Parent.

![Re-render](https://dev-to-uploads.s3.amazonaws.com/i/zycgtacuys3edzhy3c1h.png)

In the same way, I can transfer a `<Card>` into a new `<List>`.

I implement a first draft of the code and try it, I take a Card with the mouse and drag it between the various Lists. The transfer takes place, but, unfortunately, the Card component is unmounted, re-mounted, and **loses its internal state**.

Moreover the feedback from the drag animation isn't so positive. When I perform several drags quickly in succession, the App slows down and for a few moments there is a considerable **loss of frames**.

In fact, the DOM elements of the Card are recreated from scratch and this is having a negative impact on **performance**. Also, one of the elements is a scrollable `<div>` that loses its scroll position, I guess other elements such as `<video>` and `<audio>` can have similar problems.

With some effort, I can redesign the App to use Card components without a local state, but in any case I cannot avoid that the DOM elements are recreated.

### Is it possible to prevent the component from being re-mounted?
> Well, if you are reading this article it is likely that the answer is positive :), but when I asked myself the question for the first time I did not find a definitive answer, probably because it was not there yet. Let's continue with the story.

I start looking for an answer in the React repository on Github, maybe there is something useful in the issues section. I find there is a term for what I'm looking for, and it's **Reparenting**.

_"Reparenting aims to improve both the Developer and the User Experience."_

Some open [issues](https://github.com/facebook/react/issues/3965) confirm that React does not yet provide specific APIs to handle it, my hopes that something like `React.transferComponent( )` exists quickly fade away.

An approach I discover is `ReactDOM.unstable_renderSubtreeIntoContainer( )`, the name looks cool but the `unstable` tag and the fact that this API has been deprecated are enough to make me look for something else. Searches continue on Medium, Dev, and other platforms, the only possible solution seems to be the use of the [Portals](https://reactjs.org/docs/portals.html). A Tweet by Dan Abramov definitely convinces me to try them.

![Tweet](https://dev-to-uploads.s3.amazonaws.com/i/4eqmom3pyo900kod2gy8.png)

# The portals approach

I open the React documentation in the Portals section. I start reading the guide and doing some tests to get familiar with these APIs.

```js
const element = document.createElement('div');

const PortalComponent = ({children}) => {
  return ReactDOM.createPortal(children, element);
};
```

I know that I cannot move a component elsewhere in the App or it will be re-mounted, so every Child component must be part of the **same Parent**.

Should I use a portal for each Child? That way I could decide in which container element to render each of them. But how do I create containers? Do I have to write something like `document.createElement('div')` 🤨? I could instead use ref to other components. Where do I render those components? Refs are empty initially, should I force a second render? I wanted each Parent to provide a different Context, How can I do that if I am forced to use only one Parent?…

What a mess, the more I try to implement it, the more forced the approach seems to me. It doesn't give me the feeling of being very **"reactive"**, probably because portals have been designed for other purposes:

_"Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component." - React docs._

This process is more related to the DOM, at the **"React level"** the Child is still part of the same Parent, not exactly what I am looking for.

# The new solution

Maybe I'm looking for a solution in the wrong place, it is possible that, if it exists, it is more internal to React than I think.

What I know is that React represents my App with a **tree of instances**, where each instance corresponds to a component. When re-rendering a part of the App, its sub-tree is recreated and compared with the old one, so as to find the changes that have been made and update the DOM.

![ReactTree](https://dev-to-uploads.s3.amazonaws.com/i/e03gd8j1agsoxhdzys8v.png)

Due to the way this comparison is implemented, there is no way to make React **aware of the transfer** of a component. Indeed, If I try to re-render a Card component somewhere else, the result will be the unmounting of the component and the mounting of a new one.

How can I change this behavior? I could try to **interact with the internal tree**, find the instance of the Card that I want to transfer, and insert it in the new List. In this way, after a **re-rendering**, both the old and the new tree would have the transferred Card in the same place and the comparison would not cause the re-mount of the component, It might work!

Before starting to design a solution, to avoid running into dead ends I impose some **constraints** that the final result must respect:

- _It must not rely on any unstable method_
- _Reparenting must be able to work without redesigning the App_
- _It must respect the philosophy and patterns of React_

I have a solid starting point, now I have to understand how these react internals are actually **implemented**. I find out that starting from version 16, React rolled out a new implementation of that internal instances tree named **Fiber**. I read some articles about it to get a more complete picture, and when I think I have a fairly **broad view** on the topic, I start to browse the React source code in search of a solution.

After several days of testing and research, I finally have a first draft of code to try, inside a file named `react-reparenting.js`. I import it into my App, add a few lines of code, and… It **works!** The Card is not re-mounted and the goals that I have set for myself have all been respected.

This story can finally have a nice ending, I can continue the development of my App. Maybe, for the next obstacle that I will face, I will find a story like this to read.

# The end of the story

This story ends with the publication of the package on [Github](https://github.com/paol-imi/react-reparenting) and with the writing of this article. Before presenting it, I want to share with you what my vision is at the end of this project.
 
I strongly believe that **Reparenting** is not only a way of managing these situations, but **The way**, and I also believe that in the future React will implement it natively.

In my opinion, the reason why this feature has not yet been implemented is that the cases in which it is really necessary are not many. Often the elements to be transferred are stateless and very simple, so it is an **acceptable compromise** to re-mount them since the difference in performance is almost zero, and there is no state or lifecycle to be interrupted.

I'm not saying that React will implement Reparenting as it has been implemented here, or that the APIs that will be provided will be similar to these, but  I hope this package, thanks also to its simplicity, can lay the foundations for the use and diffusion of Reparenting.

_"Unmounting one component and mounting another identical one is just a simple compromise that works in most cases. The component should always be transferred, without its lifecycle being interrupted."_

<img src="https://dev-to-uploads.s3.amazonaws.com/i/wepdeu1ecqxua4v6vggh.png" width="80%"/>
<img src="https://dev-to-uploads.s3.amazonaws.com/i/0gwgvd9557j7w9saa41d.png" width="60%"/>

You can find the package on [Github](https://github.com/paol-imi/react-reparenting). On the GitHub page you will also find the documentation and links to various examples on Codesandbox.
Now let's see a simple implementation.

First, let's define the `<Child>` component, we will use a very simple one.

{% gist https://gist.github.com/Paol-imi/3216e8d74845c9565097eb5dfa4f8510 %}

Now we can use the `<Reparentable>` component, it has to be the direct parent of the children to reparent. Each `<Reparentable>` must have a unique id.

{% gist https://gist.github.com/Paol-imi/c44ee6feee0c1818cc5b4b5e0000d227 %}

Now we can reparent a `<Child>`. First we have to send its fibers using the `sendReparentableChild( )` method, then we just have to re-render the App. The transferred component will not be re-mounted.

{% gist https://gist.github.com/Paol-imi/49bb92505f6c72a89dd0d6a4b32cbd80 %}

That's all. It is also possible to create a custom Parent component and use the `<Reparentable>` inside it.

{% gist https://gist.github.com/Paol-imi/6e73cf0147c8e0a474b3ff3cf600402e %}

# Special thanks

During the development of this project, I thought that I would lose my mind managing every use case _(context, memo, some edge cases with fibers…)_. With pleasant surprise React worked in each of these cases without modification, a sign of the amazing work that the **React Team** has done over the years.

I want also to thank the **authors** of these amazing articles, without them the work would have been longer and more tedious.

- [React fiber architecture](https://github.com/acdlite/react-fiber-architecture)
- [Deep dive into react fiber internals](https://blog.logrocket.com/deep-dive-into-react-fiber-internals)
- [An in-depth overview of the new reconciliation algorithm](https://medium.com/react-in-depth/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react-e1c04700ef6e)