Consider the following example:
class GridContainer extends React.Component { ... render (){ return <div> <Pagination portalId="portal-id"></> <Grid ...> </div> } } class Grid extends React.Component { ... render (){ return <div> <div id="portal-id"></> <table ...> </div> } } class Pagination extends React.Component { ... render (){ return return ReactDOM.createPortal(<div>Paginator DOM...</div>, document.getElementById(this.props.portalId)); } }
Is it safe to render a portal inside other components DOM? I’ve tested it and it works, but I don’t know if this is reliable. The Portals
doc mention that you can render a portal in a DOM node but nothing about components DOM.
Why is this different (speculating here)? while updating the portal parent component, in the reconciliation process the diff might find the inconsistency and remove the portal node.
From my testing, the above doesn’t happen but I don’t know if I can assume that react handles it. So here comes the Q:
Is it safe to render a portal into another component DOM?
Advertisement
Answer
Is it safe? Sure, but it probably won’t work the way you expect.
First off, you can totally dump dom inside a div
that React created.
I’ve met several React programmers who would contest this fact, but Reacts design expects and accounts for editing dom directly when necessary. If they didn’t, there would be no componentDidUpdate
or React refs
.
This documentation on integration with other libraries is probably the most relevant.
Here’s the scoop:
- React won’t touch the internals of a rendered div, assuming it’s always empty at the end of render. It will only:
- Create new elements, which is irrelevant here as long as you leave it empty.
- Update existing elements that were originally created by react.
- Delete elements that were originally created by react.
So just create that empty div and leave it alone in render
and React won’t mess with it.
Here’s the rub; React guarantees nothing about the timing of the renders.
This means that you have no idea if the element will actually be there when Pagination
renders, causing the query to fail and the portal to not display. componentDidUpdate
only works because React specifically runs it after the dom is updated. But render is run before the dom is updated. So if Pagination
is rendered in the same sweep as Grid
that div is likely not mounted yet.
Now the infamous stack overflow Just-Don’t-Do-That answer:
Just don’t do that. The purpose of portal is to let you render outside the React container while keeping your components inside of Reacts render tree. If you’re rendering inside React render tree anyways why don’t you just render the components there? (if that statement is confusing I blame you)