Problem
I ran into this problem when I was working with React Portals.
In its official demo, you render into a DOM element "on the ground".
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
And when you render, you render into that node. (This is an over simplification, the original demo creates another DOM node, and append it into #modal-root. But the idea is similar enough to make the point.)
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 hard coded 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 "on the ground"
- Mutating the
.current
property doesn’t cause a re-render. Since we need to run some code when React attaches or detaches a ref to a DOM node, we need to use a callback ref instead - use a reducer for state updates because useReducer is the cheat mode of hooks, this solves a valid problem whereby mounting two pairs of slots-portals would result in the second mounted component overwriting the first one since the second
setState
call is closed with its own render
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