I am attempting to make a simple Portal wrapper that does the following.
- Can render multiple portals on the same view
- Can render multiple portals by targeting parent ids. This cannot be done by passing a ref into the component since the container may live anywhere.
- Can render multiple portal without any parent targets
This is what I came up with this morning. The code works exactly how I want it to work. It creates portals with or without a parent target and cleans up completely. However React says not to call hooks from conditions, which you can see I have done here by calling useRef within a ternary. I have not seen any warnings or errors but I am also developing this code in a custom setup with Webpack and Typescript. I have not pushed this code to NPM to see what happens when I import the library into a project. My guess is there's going to be issues. Is there a better way to do this?
Thanks in advance.
Calling Component:
<div>
{open1 && (
<Portal parentId="panel-1">
<Panel title="Poopster" onClose={handleClose1}>
<div>Panel</div>
</Panel>
</Portal>
)}
{open2 && (
<Portal>
<Panel onClose={handleClose2}>
<div>Panel</div>
</Panel>
</Portal>
)}
<div>
Portal Component
import * as React from 'react';
import { createPortal } from 'react-dom';
export interface PortalProps {
children: React.ReactElement;
parentId?: string;
}
export const Portal = ({
children,
parentId
}: PortalProps): React.ReactElement => {
let ref = !parentId ?
React.useRef(document.createElement('div')) :
React.useRef(document.getElementById(parentId));
React.useEffect((): VoidFunction => {
return (): void => {
if (!parentId && ref.current) {
document.body.removeChild(ref.current);
}
ref = null;
};
}, []);
React.useEffect(() => {
if (!parentId && ref.current) {
document.body.appendChild(ref.current);
}
}, [ref, parentId]);
return createPortal(children, ref.current);
};
export default Portal;