React usePortal钩子

创建一个portal,允许在父组件之外渲染子组件。

  • 使用useState()钩子创建一个状态变量,用于保存portal的render()remove()函数。
  • 使用ReactDOM.createPortal()ReactDOM.unmountComponentAtNode()创建一个portal和一个用于移除portal的函数。使用useCallback()钩子将这些函数包装和记忆为createPortal()
  • 使用useEffect()钩子在el的值发生变化时调用createPortal()并更新状态变量。
  • 最后,返回状态变量的render()函数。
const usePortal = el => {
  const [portal, setPortal] = React.useState({
    render: () => null,
    remove: () => null,
  });

  const createPortal = React.useCallback(el => {
    const Portal = ({ children }) => ReactDOM.createPortal(children, el);
    const remove = () => ReactDOM.unmountComponentAtNode(el);
    return { render: Portal, remove };
  }, []);

  React.useEffect(() => {
    if (el) portal.remove();
    const newPortal = createPortal(el);
    setPortal(newPortal);
    return () => newPortal.remove(el);
  }, [el]);

  return portal.render;
};

const App = () => {
  const Portal = usePortal(document.querySelector('title'));

  return (
    <p>
      Hello world!
      <Portal>Portalized Title</Portal>
    </p>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <App />
);