Support for changing a Portal's container without remounting children?
See original GitHub issue(This is related to https://github.com/facebook/react/issues/3965.)
I’m working on a project with a lot of globally unique component instances. (In other words, their key
s are essentially database IDs.) It also has DnD functionality. For reordering, everything is fine, but moving an instance from one parent to another causes a complete re-render of the instance.
Instead of moving the nodes around myself, I was thinking of using Portals. Each instance has a prop for which element ID they should render inside. Then, to reparent them, it’d be a simple case of passing a different element ID. (I’m using Redux, so it’d just be a string
in the state.)
However, changing a Portal’s container node also causes a complete re-render of everything passed to ReactDOM.createPortal
. (See this CodePen.)
Would it be possible to skip this re-rendering and effectively move the Portal contents instead?
Issue Analytics
- State:
- Created 6 years ago
- Reactions:38
- Comments:28 (8 by maintainers)
@gaearon @benwiley4000 I hit this too & fixed it for myself. Rather than put together an example, I’ve turned the basic pattern you’re describing into a library you can use to solve this properly.
It’s effectively the concept defined above, with a few additions, and lets you define some content in one place, render & mount it there once, then place it elsewhere and move it later (potentially out of the page entirely, and back) all without remounting or necessarily rerendering.
I’m calling it reverse portals, since it’s the opposite model to normal portals: instead of pushing content from a React component to distant DOM, you define content in one place, then declaratively pull that DOM into your React tree elsewhere. I’m using it in production to reparent expensive-to-initialize components, it’s working very nicely for me!
Repo is over here: https://github.com/httptoolkit/react-reverse-portal. Let me know if it works for you!
We had a similar problem and solved it in userland with a combination of portals, context, and refs. It’s a bit convoluted but it works. The idea is that you have state and a “node manager” live at the top. Your leaf component, when it mounts, passes its “leaf div” up the context to the “node manager”. In return, the “node manager” gives the leaf component a node to portal into. That node would always be the same. Node manager would create it once. Initially, node manager would attach it to the leaf’s own “leaf div”. But it can also detach it and attach it somewhere else without recreating the element. From leaf’s point of view, it always renders the portal into the same node. But the thing at the top can move that node from an actual div (originally in the leaf) to any other div on the page. Instead of reparenting the portal itself, we always portal into the same node, but that node itself is moved in the document imperatively wherever we need.