一、React Portal 基础介绍

在 React 里,组件的渲染默认是按照组件树的结构来的,也就是子组件会被渲染到父组件的 DOM 节点里。不过有时候,我们希望把组件渲染到 DOM 树里的其他位置,这时候 React Portal 就派上用场啦。

1.1 什么是 React Portal

React Portal 能让我们把组件渲染到当前组件树之外的 DOM 节点上。简单来说,就是可以让组件在不改变其逻辑位置的情况下,渲染到其他地方。比如,模态框、下拉菜单、通知组件这些,用 React Portal 来处理就很合适。

1.2 基本语法

在 React 里使用 Portal 很简单,通过 ReactDOM.createPortal 方法就能创建。这个方法接收两个参数,第一个是要渲染的子元素,第二个是目标 DOM 节点。下面是个简单的示例:

// React 技术栈
import React from 'react';
import ReactDOM from 'react-dom';

// 创建一个组件
const MyPortalComponent = () => {
  // 获取目标 DOM 节点,这里假设在 HTML 里有一个 id 为 'portal-root' 的元素
  const portalRoot = document.getElementById('portal-root');
  return ReactDOM.createPortal(
    // 要渲染的子元素
    <div>这是通过 Portal 渲染的内容</div>,
    // 目标 DOM 节点
    portalRoot
  );
};

export default MyPortalComponent;

在这个示例里,MyPortalComponent 组件会把 <div> 元素渲染到 portal-root 这个 DOM 节点里。

二、在模态框中使用 React Portal

2.1 模态框的应用场景

模态框是网页里很常见的交互组件,一般用于显示重要信息、确认操作之类的。在模态框显示的时候,会阻止用户和页面其他部分进行交互。

2.2 传统模态框的问题

如果不用 React Portal 来实现模态框,模态框会被渲染到父组件的 DOM 节点里。这样可能会受到父组件样式的影响,比如父组件设置了 overflow: hidden,模态框可能就显示不全。

2.3 使用 React Portal 实现模态框

下面是一个使用 React Portal 实现模态框的示例:

// React 技术栈
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

// 创建模态框组件
const Modal = ({ isOpen, onClose, children }) => {
  if (!isOpen) return null;
  // 获取目标 DOM 节点
  const portalRoot = document.getElementById('modal-root');
  return ReactDOM.createPortal(
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        {children}
        <button onClick={onClose}>关闭</button>
      </div>
    </div>,
    portalRoot
  );
};

// 创建一个使用模态框的组件
const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const openModal = () => setIsModalOpen(true);
  const closeModal = () => setIsModalOpen(false);
  return (
    <div>
      <button onClick={openModal}>打开模态框</button>
      <Modal isOpen={isModalOpen} onClose={closeModal}>
        <h2>这是一个模态框</h2>
        <p>模态框里的内容</p>
      </Modal>
    </div>
  );
};

export default App;

在这个示例里,Modal 组件通过 ReactDOM.createPortal 把模态框渲染到 modal-root 这个 DOM 节点里。这样模态框就不会受到父组件样式的影响啦。

2.4 模态框使用 React Portal 的优点

  • 不受父组件样式影响:就像上面说的,不会因为父组件的样式设置而显示不全。
  • 更好的层级管理:模态框可以独立于组件树进行层级管理,避免了层级嵌套带来的问题。

2.5 模态框使用 React Portal 的注意事项

  • 目标 DOM 节点的存在:要确保目标 DOM 节点在渲染之前就已经存在,不然会报错。
  • 事件处理:要注意事件冒泡的问题,比如在模态框里点击关闭按钮,要阻止事件冒泡,不然可能会触发模态框的关闭事件。

三、在通知组件中使用 React Portal

3.1 通知组件的应用场景

通知组件一般用于给用户展示一些提示信息,比如操作成功、失败的提示,新消息提醒之类的。

3.2 传统通知组件的问题

传统的通知组件如果直接渲染在组件树里,可能会受到父组件布局的影响,而且在页面滚动的时候,通知组件的位置可能会发生变化。

3.3 使用 React Portal 实现通知组件

下面是一个使用 React Portal 实现通知组件的示例:

// React 技术栈
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

// 创建通知组件
const Notification = ({ message, duration }) => {
  const [isVisible, setIsVisible] = useState(true);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsVisible(false);
    }, duration);
    return () => clearTimeout(timer);
  }, [duration]);
  if (!isVisible) return null;
  // 获取目标 DOM 节点
  const portalRoot = document.getElementById('notification-root');
  return ReactDOM.createPortal(
    <div className="notification">
      <p>{message}</p>
    </div>,
    portalRoot
  );
};

// 创建一个使用通知组件的组件
const App = () => {
  const [showNotification, setShowNotification] = useState(false);
  const showNotif = () => {
    setShowNotification(true);
    setTimeout(() => {
      setShowNotification(false);
    }, 3000);
  };
  return (
    <div>
      <button onClick={showNotif}>显示通知</button>
      {showNotification && <Notification message="这是一条通知" duration={3000} />}
    </div>
  );
};

export default App;

在这个示例里,Notification 组件通过 ReactDOM.createPortal 把通知内容渲染到 notification-root 这个 DOM 节点里。这样通知组件就不会受到父组件布局的影响,而且在页面滚动的时候,通知组件的位置也不会发生变化。

3.4 通知组件使用 React Portal 的优点

  • 独立于父组件布局:不会受到父组件布局的影响,能保证通知组件在页面的固定位置显示。
  • 更好的用户体验:在页面滚动的时候,通知组件的位置不会发生变化,用户能更方便地看到通知信息。

3.5 通知组件使用 React Portal 的注意事项

  • 通知的显示和隐藏:要控制好通知的显示和隐藏时间,避免通知一直显示或者显示时间过短。
  • 样式设置:要设置好通知组件的样式,比如位置、背景颜色、字体颜色等,让通知组件更美观。

四、React Portal 的技术优缺点总结

4.1 优点

  • 灵活的渲染位置:可以把组件渲染到 DOM 树的任意位置,不受组件树结构的限制。
  • 避免样式冲突:能避免父组件样式对子组件的影响,让组件的样式更独立。
  • 更好的层级管理:方便进行层级管理,避免层级嵌套带来的问题。

4.2 缺点

  • 增加复杂度:使用 React Portal 会增加代码的复杂度,尤其是在处理事件和状态管理的时候。
  • 目标 DOM 节点的管理:需要手动管理目标 DOM 节点的创建和销毁,增加了一定的工作量。

五、注意事项

5.1 目标 DOM 节点的管理

在使用 React Portal 时,要确保目标 DOM 节点在渲染之前就已经存在。可以在 HTML 文件里手动创建目标 DOM 节点,也可以在 React 组件里动态创建。

5.2 事件处理

要注意事件冒泡的问题,在处理事件的时候,要根据实际情况阻止事件冒泡,避免出现意外的情况。

5.3 性能问题

虽然 React Portal 能带来很多好处,但是如果滥用,可能会影响性能。比如频繁创建和销毁目标 DOM 节点,会增加浏览器的负担。

六、文章总结

React Portal 是 React 里一个很有用的特性,它能让我们把组件渲染到 DOM 树的其他位置,在模态框和通知组件这些场景里非常实用。通过使用 React Portal,我们可以避免父组件样式对子组件的影响,实现更好的层级管理。不过,在使用 React Portal 的时候,也要注意目标 DOM 节点的管理、事件处理和性能问题。