DEV Community

Cover image for ⚛️ Reparenting is now possible with React

⚛️ Reparenting is now possible with React

Paolo Longo on May 23, 2020

Originally published on Medium I am designing an app similar to Trello. On the main page, I want some vertical Lists and some Cards that can be dr...
Collapse
 
miketalbot profile image
Mike Talbot ⭐

Very interesting read and nice work on the API.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Replying to myself, but having read this excellent article I dug into some of the linked references which got me very excited about utilising browser idle time. (It's what Fiber is up to)

I grabbed the keyboard and crafted a bit of a library for it - then wrote this article announcing it.

Thanks again for the inspiration Paolo!

Collapse
 
paolimi profile image
Paolo Longo

Your project seems very interesting, Nice to have contributed to your Eureka moment :)

Collapse
 
joserfelix profile image
Jose Felix

Really interesting! Thanks for this article showcasing the development process. One alternate solution to this would be to use an orthogonal state. You should check out Recoil which is an implementation of the orthogonal state by the Facebook team. You could also look at this video from the creator which shows something similar.

Collapse
 
smona profile image
Mel Bourgeois

Or, if you like a mature solution, you can use Redux ;). While it's possible to not lose state with existing tools, the value of this library seems to be that you can now move a component to a different parent and re-use the same dom nodes. No centralized state solution can manage that! (afaik)

Collapse
 
joserfelix profile image
Jose Felix

Redux wouldn't really work since every time you create a new list item it will have to re-render the tree. In this case, the best solution would be to use reparenting or orthogonal state.

Thread Thread
 
smona profile image
Mel Bourgeois

Maybe I misunderstood recoil. My understanding is that if you have two state atoms with lists of items, and you move an item from one list to another, the same dom elements will not be reused with separate parents rendering like this:

<List>{taskList1.map(t => <Mytask task={t} />)}</List>
<List>{taskList2.map(t => <Mytask task={t} />)}</List>

If recoil does support reusing dom elements across parents, please let me know! I didn't find anything about it in the docs, so i don't see how it would be any more suited to this task than redux. My understanding of redux vs recoil is that it comes down to whether you want your state centralized or not.

Thread Thread
 
joserfelix profile image
Jose Felix

Instead of only creating an atom for every list, you can create an atom for each item. This means that it will be separate from the list state and whenever you change its state you only affect that item. Furthermore, with selectors, you can prevent the list from re-rendering where it doesn't need to. Redux does not make this easy since it has a centralized state.

Thread Thread
 
smona profile image
Mel Bourgeois

Child components with the same key are not reused across separate parents. Whether you're storing the items as separate atoms and storing lists of ids in another atom (which, btw is simply normalization, a practice that's extremely common and simple with Redux) or not. React-Redux also uses selectors to determine the props that will be passed to components based on the centralized state, and if the props haven't changed, the component won't re-render. There are also libraries like reselect for setting up multiple layers of memoization.

The issue here isn't preventing unnecessary renders, it's preserving dom nodes of a child component when reparenting it. Neither Redux nor Recoil can accomplish that on their own.

Collapse
 
smona profile image
Mel Bourgeois

Very cool stuff! And a good reminder that "impossible" always has some give. I've been having trouble building a custom drag interaction that moves components across parents without killing the interaction, hopefully this will do the trick 🤞

Collapse
 
smona profile image
Mel Bourgeois • Edited

It worked like a charm :). I'm using react-reparenting with react-draggable for preserving drag interactions on components that are being moved between parent containers. It doesn't preserve useState, but that's perfectly acceptable for my use case. Thank you!!

Collapse
 
bduffany profile image
Brandon Duffany • Edited

Nice article!

For readers like me who are looking for a way to reparent an expensive element without re-rendering, note that this solution DOES trigger a re-render, so the actual DOM node is destroyed and re-built. So you may want to look elsewhere. (I'm still looking for a solution myself)

Collapse
 
paolimi profile image
Paolo Longo

The aim of the project is exactly to avoid this. The re-render takes place but the element is not reconstructed thanks to the work that is done at the React internals.

Collapse
 
saswatamcode profile image
Saswata Mukherjee

Awesome API!

Collapse
 
paulfidika profile image
Paul Fidika

Wow thanks for taking the time to write up this article and create this open-source repo! I'm considering using it for my current project.