Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to inject react context to div outside the root in react (under the body)

I am working with react cytoscape library. Try to integrate to popper plugin of cytoscape. the popper "content" property expect to return a div element describe the popper which appended to the "body" element. Since the context provider is under the root element , that div can't be consumer of that context. How can I use the same context also in the popper element which define outside the root.

Working with react component for cytoscape but the plugins of cytoscape are in pure js.

<body>
 <div id=root>
  <Style_Provider>
      .
      .
      .
  </Style_Provider>
 </div>
 <div id="popper">
    // need to access to style context
 </div>
</body>
like image 740
cryptoun Avatar asked Oct 30 '25 22:10

cryptoun


1 Answers

As Eric Hasselbring said, portals address this use case:

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

Here's an example, using context:

const ExampleContext = React.createContext(
    "default context"
);

class Example extends React.Component {
    render() {
        return (
            <ExampleContext.Provider value="context from Example">
              <div>
                  This is the parent comnponent.
                  <MyPortal />
              </div>
            </ExampleContext.Provider>
        );
    }
}

class MyPortal extends React.Component {
    static contextType = ExampleContext;
    render() {
        return ReactDOM.createPortal(
            <div>This is "my portal," context stuff is "{this.context}"</div>,
            document.getElementById("portal")
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<hr>
<div id="portal"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

In a comment you said:

the popper creates those 'divs' dynamically. Hence, I cant create 'MyPortal' component initially.

Just make the creation of the portal conditional. Here's the above, but with a button to show/hide the portal:

const ExampleContext = React.createContext(
    "default context"
);

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            showPortal: false
        };
        this.showHideClick = this.showHideClick.bind(this);
    }

    showHideClick() {
        this.setState(({showPortal}) => ({showPortal: !showPortal}));
    }

    render() {
        const {showPortal} = this.state;
        return (
            <ExampleContext.Provider value="context from Example">
              <div>
                  This is the parent comnponent.
                  <input type="button" value={showPortal ? "Hide" : "Show"} onClick={this.showHideClick}/>
                  {showPortal && <MyPortal />}
              </div>
            </ExampleContext.Provider>
        );
    }
}

class MyPortal extends React.Component {
    static contextType = ExampleContext;
    render() {
        return ReactDOM.createPortal(
            <div>This is "my portal," context stuff is "{this.context}"</div>,
            document.getElementById("portal")
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<hr>
<div id="portal"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
like image 119
T.J. Crowder Avatar answered Nov 01 '25 11:11

T.J. Crowder