Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a reverse react portal

React portals let you render a React child into a different DOM element, somewhere totally separate on the page.

I want to do the reverse: I want to render a DOM element once, elsewhere on the page, and then pull it into my React DOM output depending on my react tree.

The specific use case for this is Monaco Editor. It's a complex component, which is expensive to render from scratch, but cheap to update. In my application, the editor appears/disappears as users open/close different content. That means React creates the component from scratch each time new content is opened, and the delay to create a new editor makes that action a slower than I'd like.

I'd like to render it once, hide it somewhere, and then show it in the right place in my DOM when & where it's needed. I know that I only need one instance at any time.

like image 306
Tim Perry Avatar asked Aug 23 '19 07:08

Tim Perry


People also ask

How do you create a React portal?

A React Portal can be created using the createPortal function imported from react-dom . It takes two arguments: content : any valid renderable React element. containerElement : a valid DOM element to which we can append the content.

How do React portals work?

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. The first argument ( child ) is any renderable React child, such as an element, string, or fragment. The second argument ( container ) is a DOM element.

Are React portals important?

We mainly need portals when a React parent component has a hidden value of overflow property(overflow: hidden) or z-index style, and we need a child component to openly come out of the current tree hierarchy.


1 Answers

In the end I built a library to do it myself: https://github.com/httptoolkit/react-reverse-portal.

Render your content in one place, once, and pass it around and insert it into the DOM somewhere else without rerendering.

Example:

import * as portals from 'react-reverse-portal';

const MyComponent = (props) => {
    const portalNode = React.useMemo(() => portals.createPortalNode());

    return <div>
        <portals.InPortal node={portalNode}>
            <MyExpensiveComponent
                myProp={"defaultValue"}
            />
        </portals.InPortal>

        { props.componentToShow === 'component-a'
            ? <ComponentA portalNode={portalNode} />
            : <ComponentB portalNode={portalNode} /> }
    </div>;
}

const ComponentA = (props) => {
    return <div>
        A:
        <portals.OutPortal node={props.portalNode} />
    </div>;
}

const ComponentB = (props) => {
    return <div>
        B:
        <portals.OutPortal
            node={props.portalNode}
            myProp={"newValue"}
            myOtherProp={123}
        />
    </div>;
}
like image 182
Tim Perry Avatar answered Nov 13 '22 04:11

Tim Perry