React Portals 有什么用

React PortalsReact提供的一种机制,它允许开发者将组件渲染到DOM树中的不同位置,而不受组件层次结构的限制。React Portals的主要用途和优势包括以下几个方面:

用途和优势

  1. 处理全局UI元素
    React Portals允许将UI元素渲染到应用的根DOM之外,这对于创建全局的UI元素非常有用。例如,模态框、通知框、工具提示等全局元素可以浮在应用的其他组件之上,而不会受到组件嵌套结构的影响。这有助于实现更加灵活和动态的UI设计。

  2. 处理层叠上下文

    在CSS中,某些样式属性(如z-index)会创建层叠上下文,从而限制某些元素的显示顺序。使用React Portals可以将元素渲染到指定的DOM节点上从而绕过这些层叠上下文的限制。这有助于实现更复杂的UI布局,特别是在需要控制元素显示顺序的场景中。

  3. 处理复杂的UI布局

    在某些情况下,需要将组件的渲染内容插入到DOM结构的特定位置,以满足设计或布局需求。React Portals允许在不改变组件层次结构的情况下实现这些需求。例如,可以将悬浮菜单或侧边栏等组件渲染到DOM树的特定位置,而不必担心它们会破坏组件的层次结构。

  4. 提高组件的可重用性

    使用React Portals可以将通用的UI组件(如模态框或通知框)封装为可重用的组件。这些组件可以在不同的应用中使用,并且不需要关心它们的放置位置。这有助于提高代码的可维护性和复用性。

  5. 实现更灵活的DOM操作

    React Portals提供了一种有效的方法来渲染子节点到存在于父组件DOM层次结构之外的DOM节点中。这有助于打破传统父子组件层级的限制,实现更加灵活和动态的DOM操作。例如,可以在应用程序的不同部分之间共享和重用模态组件,而无需担心它们的层级关系。

  6. 简化事件处理和状态管理

    尽管组件被渲染到了不同的DOM节点,但React Portals仍然能够保持组件的正常生命周期和状态管理。这意味着可以在Portal内部使用状态、事件处理程序等React功能。此外,由于事件可以在Portals中正常冒泡,因此可以简化事件管理,使构建具有复杂交互的组件变得更加容易

使用注意事项

合适的目标容器 :确保选择一个合适的DOM元素作为Portal的目标容器。通常会在组件的componentDidMount生命周期方法中将Portal添加到DOM,并在componentWillUnmount中将其移除。

  • 避免滥用 :虽然React Portals提供了灵活性,但不应滥用它们。只有在必要的情况下使用Portal,以避免过度复杂的嵌套结构
  • 层叠上下文管理 :Portal可能会破坏默认的CSS层叠上下文。如果Portal内容和其他元素有层叠关系,可能需要手动管理z-index或使用CSS属性来控制渲染顺序。
  • 事件处理 :由于Portal的内容可以渲染在组件树之外,因此事件处理可能会受到限制。确保事件处理程序适用于Portal内容,或者使用事件冒泡机制来处理事件

React Portals优缺点的详细分析

  • 优点

    • 处理全局或跨层级的UI元素:
      • React Portals能够轻松处理模态框、通知框、下拉菜单等全局或跨层级的UI元素,而不会破坏组件的层次结构。
      • 这些元素可以浮在应用的其他组件之上,提供更好的用户体验。
    • 解决CSS层叠顺序问题:
      • 通过将组件渲染到DOM树的特定位置,React Portals可以避免由于CSS层叠顺序导致的布局问题。
      • 这有助于实现更复杂的UI布局和样式控制。
    • 提高组件的可重用性:
      • 使用React Portals可以将通用的UI组件(如模态框或通知框)封装为可重用的组件。
      • 这些组件可以在不同的应用中使用,并且不需要关心它们的放置位置,从而提高代码的可维护性和复用性。
    • 简化DOM操作:
      • React Portals提供了一种有效的方法来渲染子节点到存在于父组件DOM层次结构之外的DOM节点中。
      • 这有助于打破传统父子组件层级的限制,实现更加灵活和动态的DOM操作。
    • 保持组件的正常生命周期和状态管理:
      • 尽管组件被渲染到了不同的DOM节点,但React Portals仍然能够保持组件的正常生命周期和状态管理。
      • 这意味着可以在Portal内部使用状态、事件处理程序等React功能,从而简化事件处理和状态管理。
  • 缺点

    • 可能增加应用的复杂性:
      • 虽然React Portals使得模态框和工具提示等组件的实现更简单,但同时也增加了应用的复杂性。
      • 开发者需要更加小心地管理状态和事件,因为组件的渲染位置与组件的层次结构不再一致。
    • 事件冒泡可能变得更加复杂:
      • 在使用React Portals时,某些事件的冒泡可能会变得更加复杂。
      • 由于Portals在DOM树中的位置与React树中的位置不一致,因此事件可能会在不期望的组件中触发或停止冒泡。
    • 性能开销:
      • 使用React Portals可能会导致额外的DOM节点,这可能在某些情况下带来性能开销。
      • 虽然对于大多数应用而言,这种影响微乎其微,但在性能敏感的应用中需要考虑这一点。
    • 需要额外的DOM节点:
      • React Portals需要有一个真实的DOM节点作为挂载点。
      • 这意味着开发者需要在应用中为Portals预留合适的DOM节点,这可能会增加一些额外的开发工作。

实例:全局通知框(Notification)的实现

在这个实例中,我们将使用React Portals来实现一个全局通知框组件。通知框通常需要在应用的任何内容之上显示,并且可能会频繁地出现和消失,因此它是一个很好的React Portals使用场景

  1. 创建NotificationManager组件

    首先,我们创建一个NotificationManager组件,它将负责管理通知框的显示和隐藏。这个组件将使用React的上下文(Context)API来提供一个全局的通知框状态和方法

    javascript 复制代码
    // NotificationManager.jsx
    import React, { createContext, useState, useEffect } from 'react';
    import ReactDOM from 'react-dom';
     
    const NotificationContext = createContext();
     
    export const NotificationProvider = ({ children }) => {
      const [notifications, setNotifications] = useState([]);
     
      // 创建一个容器用于挂载Portal
      const container = document.createElement('div');
      document.body.appendChild(container);
     
      useEffect(() => {
        return () => {
          document.body.removeChild(container);
        };
      }, []);
     
      const addNotification = (message) => {
        setNotifications((prevNotifications) => [
          ...prevNotifications,
          { id: Date.now(), message },
        ]);
     
        // 自动移除通知(模拟)
        setTimeout(() => {
          setNotifications((prevNotifications) =>
            prevNotifications.filter((notification) => notification.id !== Date.now())
          );
        }, 3000);
      };
     
      const value = { notifications, addNotification };
     
      return (
        <NotificationContext.Provider value={value}>
          {children}
          {ReactDOM.createPortal(
            <div className="notification-container">
              {notifications.map((notification) => (
                <div key={notification.id} className="notification">
                  {notification.message}
                </div>
              ))}
            </div>,
            container
          )}
        </NotificationContext.Provider>
      );
    };
     
    export const useNotifications = () => React.useContext(NotificationContext);
  2. 添加样式

    接下来,我们为通知框添加一些基本样式。

    javascript 复制代码
    /* styles.css */
    .notification-container {
      position: fixed;
      top: 20px;
      right: 20px;
      width: 300px;
      z-index: 1000;
    }
     
    .notification {
      padding: 10px;
      margin-bottom: 10px;
      background-color: #fff;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      border-radius: 4px;
      opacity: 0;
      transform: translateY(20px);
      animation: slideIn 0.3s forwards ease-in-out;
    }
     
    @keyframes slideIn {
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
  3. 使用NotificationManager组件

    现在,我们可以在应用的其他部分使用这个NotificationManager组件,并通过上下文API来添加通知

    javascript 复制代码
    // App.jsx
    import React from 'react';
    import { NotificationProvider, useNotifications } from './NotificationManager';
    import './styles.css';
     
    const NotifyButton = () => {
      const { addNotification } = useNotifications();
     
      const handleClick = () => {
        addNotification('This is a notification message!');
      };
     
      return <button onClick={handleClick}>Notify Me</button>;
    };
     
    function App() {
      return (
        <NotificationProvider>
          <div>
            <h1>My App</h1>
            <NotifyButton />
          </div>
        </NotificationProvider>
      );
    }
     
    export default App;

总结

  • 在React 18中,我们使用React Portals来实现了一个全局通知框组件。NotificationManager组件创建了一个容器,并将其添加到document.body的末尾。然后,它使用ReactDOM.createPortal方法将通知框的内容渲染到这个容器上。这样,通知框就可以浮在应用的其他内容之上,而不会受到组件层次结构的限制

  • 我们还使用了React的上下文(Context)API来提供一个全局的通知框状态和方法,这样应用中的任何组件都可以轻松地添加通知。

值得注意的是,虽然React 18引入了并发模式和新的更新机制,但React Portals的基本使用方式并没有改变。然而,在并发模式下,React可能会更加高效地处理Portals的更新和渲染,特别是在处理频繁出现和消失的通知框等UI元素时

相关推荐
m0_748248771 小时前
【前端 Uniapp】使用Vant打造Uniapp项目(避坑版)
前端·uni-app
深海的鲸同学 luvi2 小时前
高德地图离线加载解决方案(内网部署)+本地地图瓦片加载
前端·javascript·html5
码字哥3 小时前
EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
java·服务器·前端
GIS好难学4 小时前
《Vue进阶教程》第六课:computed()函数详解(上)
前端·javascript·vue.js
nyf_unknown4 小时前
(css)element中el-select下拉框整体样式修改
前端·css
m0_548514774 小时前
前端打印功能(vue +springboot)
前端·vue.js·spring boot
执键行天涯5 小时前
element-plus中的resetFields()方法
前端·javascript·vue.js
Days20505 小时前
uniapp小程序增加加载功能
开发语言·前端·javascript
喵喵酱仔__5 小时前
vue 给div增加title属性
前端·javascript·vue.js
dazhong20125 小时前
HTML前端开发-- Iconfont 矢量图库使用简介
前端·html·svg·矢量图·iconfont