import * as React from "react"; import { ComponentProps } from "react"; import { InPortal } from "react-reverse-portal"; const LazyPortalTouched = Symbol("LazyPortalTouched"); const CurrentLazyPortal = Symbol("CurrentLazyPortal"); /** * A `react-reverse-portal` `` that defers its first render until it is used by a mounted ``. * * Package requirements: * - "react-reverse-portal": "^2.1" */ export class LazyInPortal extends React.PureComponent> { touchNode(oldNode?: ComponentProps["node"]) { const newNode = this.props.node; if (oldNode == newNode) return; if (oldNode && oldNode[CurrentLazyPortal] == this) { oldNode[CurrentLazyPortal] = null; } if (!newNode[LazyPortalTouched]) { const { mount } = newNode; newNode[LazyPortalTouched] = true; newNode.mount = (...args) => { const ret = mount(...args); if (newNode[CurrentLazyPortal]) { (newNode[CurrentLazyPortal] as this).onOutputMounted(); } return ret; } } newNode[CurrentLazyPortal] = this; } componentDidMount(): void { this.touchNode(); } componentDidUpdate(prevProps: Readonly>) { this.touchNode(prevProps.node); } componentWillUnmount(): void { const node = this.props.node; if (node) { node[CurrentLazyPortal] = null; } } onOutputMounted() { if (!this._rendered) this.forceUpdate(); } private _rendered = false; render() { const { node } = this.props; if (this._rendered || node.element.isConnected) { this._rendered = true; return } return null; } }