TypeScript-React 项目中的 Portals 使用指南

TypeScript-React 项目中的 Portals 使用指南

react Cheatsheets for experienced React developers getting started with TypeScript react 项目地址: https://gitcode.com/gh_mirrors/rea/react

什么是 React Portals

Portals 是 React 提供的一种特殊功能,允许开发者将子节点渲染到存在于父组件 DOM 层次结构之外的 DOM 节点中。这在创建模态框、弹出窗口、工具提示等需要脱离常规 DOM 流的组件时特别有用。

为什么需要 Portals

在 React 应用中,组件通常按照其父组件-子组件的关系渲染到 DOM 中。然而,某些情况下我们需要将子组件渲染到 DOM 树的其他位置,例如:

  • 模态对话框需要显示在其他内容之上
  • 工具提示需要避免被父组件的 overflow:hidden 截断
  • 全局通知需要独立于组件层级

Portals 完美解决了这些问题,同时保持了 React 的事件冒泡机制。

基础实现:类组件方式

在 TypeScript-React 项目中,我们可以这样实现一个 Portal 组件:

const modalRoot = document.getElementById("modal-root") as HTMLElement;
// 确保你的 HTML 文件中有一个 id 为 'modal-root' 的 div

export class Modal extends React.Component<{ children?: React.ReactNode }> {
  el: HTMLElement = document.createElement("div");

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(this.props.children, this.el);
  }
}

代码解析

  1. 首先我们获取目标 DOM 节点(modal-root)
  2. 在类组件中创建一个新的 div 元素作为 Portal 的容器
  3. 在组件挂载时将容器添加到 modal-root
  4. 在组件卸载时移除容器
  5. 使用 ReactDOM.createPortal 将子组件渲染到容器中

现代实现:函数组件与 Hooks

随着 React Hooks 的普及,我们可以使用更简洁的函数组件方式实现相同的功能:

import { useEffect, useRef, ReactNode } from "react";
import { createPortal } from "react-dom";

const modalRoot = document.querySelector("#modal-root") as HTMLElement;

type ModalProps = {
  children: ReactNode;
};

function Modal({ children }: ModalProps) {
  // 使用 useRef 保持容器元素的引用
  const elRef = useRef<HTMLDivElement | null>(null);
  if (!elRef.current) elRef.current = document.createElement("div");

  useEffect(() => {
    const el = elRef.current!;
    modalRoot.appendChild(el);
    return () => {
      modalRoot.removeChild(el);
    };
  }, []);

  return createPortal(children, elRef.current);
}

代码解析

  1. 使用 useRef 来保持对容器元素的引用
  2. 在组件首次渲染时创建容器元素
  3. 使用 useEffect 处理挂载和卸载逻辑
  4. 返回通过 createPortal 创建的 Portal

使用示例

下面是一个完整的 Modal 组件使用示例:

import { useState } from "react";

function App() {
  const [showModal, setShowModal] = useState(false);

  return (
    <div>
      {/* 这个 div 也可以放在你的静态 HTML 文件中 */}
      <div id="modal-root"></div>
      {showModal && (
        <Modal>
          <div
            style={{
              display: "grid",
              placeItems: "center",
              height: "100vh",
              width: "100vw",
              background: "rgba(0,0,0,0.1)",
              zIndex: 99,
            }}
          >
            I'm a modal!{" "}
            <button
              style={{ background: "papyawhip" }}
              onClick={() => setShowModal(false)}
            >
              close
            </button>
          </div>
        </Modal>
      )}
      <button onClick={() => setShowModal(true)}>show Modal</button>
      {/* 应用的其余部分 */}
    </div>
  );
}

注意事项

  1. 事件冒泡:通过 Portal 渲染的元素仍然存在于 React 树中,因此事件会冒泡到 React 树中的祖先组件,而不是 DOM 树中的祖先。

  2. 生命周期:Portal 中的组件仍然遵循 React 组件的生命周期。

  3. SSR:在服务器端渲染时需要注意 Portal 的使用,因为 document 对象在服务器端不可用。

  4. 性能:频繁创建和销毁 Portal 可能会影响性能,考虑复用 Portal 容器。

  5. 可访问性:确保 Portal 内容对屏幕阅读器等辅助技术友好。

最佳实践

  1. 为 Portal 容器添加适当的 ARIA 属性以提高可访问性
  2. 考虑使用 CSS 固定定位或绝对定位来确保 Portal 内容显示在正确位置
  3. 对于频繁使用的 Portal,考虑使用状态管理来全局控制
  4. 在 TypeScript 中明确定义 Portal 组件的 props 类型

通过掌握 Portals 的使用,你可以在 TypeScript-React 项目中创建更灵活、更强大的用户界面组件。

react Cheatsheets for experienced React developers getting started with TypeScript react 项目地址: https://gitcode.com/gh_mirrors/rea/react

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈书苹Peter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值