Portal into a React Component

Problem

I ran into this problem when I was working with React Portals.

Normally, when using React Portal, we prepare an HTML root element for the portal to be rendered into. In its example from the React official doc, the app renders into #app-root and the portal element renders into #modal-root:

<html>
  <body>
    <div id="app-root"></div>
    <div id="modal-root"></div>
  </body>
</html>

Note: This is a simplified mind model, the original demo creates another DOM node, and append it into #modal-root.

render() {
  return ReactDOM.createPortal(
    this.props.children,
    document.getElementById('modal-root')
  );
}

Now, what happens if the target element for the portal to render into is not initially rendered in the HTML, but is another React component instead, and it may or may not have been mounted by the time the portal tries to render into it?

Approach

In a few words, the approach is that

  • use refs instead of DOM selectors to find the slots
  • maintain refs to all possible slots in an object and put the object & setter (dispatcher) in context

I have a demo in this CodeSandbox and below is a little illustration:

There is an older but similar implementation of this using class components. It's open sourced as a library React Slots Fill.

Implementation notes

This section is not yet written, some bullet points:

  • refs allow access to DOM nodes
  • mutating the .current property of a ref doesn’t trigger a re-render. In order to run some code immediately when React attaches or detaches a ref to a DOM node, we need to use a callback ref
  • use a reducer for state updates because useReducer is the cheat mode of hooks, this solves the problem where mounting two slot-portal pairs resulting in the second mounted component overwriting the first one since the second setState call is closed with its own render cycle

Thoughts

This section is also not yet written, some bullet points:

  • refs are "escape hatch" so not sure if this whole thing is necessarily a good idea
  • React used to have "string ref", and why it's advised against
  • also not necessarily a good idea to (systematically) put stateful variables with complex update schemes in context

Relevant links