On usePortal

A React Hook to make using Portals easier.

Here's a gist showing a usePortal hook to make using React Portals easier.

import {
  createPortal as createReactPortal,
  unmountComponentAtNode,
} from "react-dom";
import { useState, useCallback, useEffect } from "react";

export let usePortal = (el: Element = document.body) => {
  let [portal, setPortal] = useState<{
    render: Function;
    remove: Function;
  }>({
    render: () => null,
    remove: () => null,
  });

  const createPortal = useCallback((el) => {
    let Portal = ({ children }: { children: Element }) =>
      createReactPortal(children, el);
    let remove = () => unmountComponentAtNode(el);
    return { render: Portal, remove };
  }, []);

  useEffect(() => {
    if (el) unmountComponentAtNode(el);
    let newPortal = createPortal(el);
    setPortal(createPortal(el));
    return () => {
      newPortal.remove();
    };
  }, [el, createPortal]);

  return portal.render;
};

Example

import { usePortal } from "somewhere";

let Foo = () => {
  let Portal = usePortal(/* maybe a document.querySelector call */);

  return <Portal>{/* something */}</Portal>;
};