4

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>
Stephan T.
  • 5,843
  • 3
  • 20
  • 42
cryptoun
  • 43
  • 6

1 Answers1

5

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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • the popper creates those 'divs' dynamically. Hence, I cant create 'MyPortal' component initially. – cryptoun Aug 13 '19 at 14:15
  • @cryptoun - You don't have to. Just do it when it's appropriate. I've added an example of making it conditional above. – T.J. Crowder Aug 13 '19 at 15:36