React createPortal 弹窗 modal

本文介绍了如何在React中使用createPortal技术实现弹窗组件。重点在于理解Portal的使用,通过创建一个hook usePortal来获取附加到body的DOM节点,并结合CSS动画实现平滑的显示和隐藏效果。最后展示了如何根据业务需求定制弹窗样式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我对弹窗的设想是:

  1. 可定制宽高,append 到 body,fixed 定位
  2. 容易定制样式,来适应不断变化的多种多样的需求

在 React 中,要实现第一点,可用到 Portal 技术。

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

ReactDOM.createPortal(child, container)

第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个
DOM 元素。

具体用法可查看 官网 Portals 的文档。

对此,我想先实现的是一个 hook usePortal,它的功能就是返回一个 append 到 body 下的结点。

import {
   
    useRef, useEffect } from 'react'

/**
 * 将元素 append 到 body
 * @param {HTMLElement} rootElem
 */
function appendToBody(rootElem) {
   
   
  document.body.appendChild(rootElem)
}

export default function usePortal() {
   
   
  const rootElemRef = useRef(null)

  // 延迟创建节点,而不是在初始化传入,
  // 这样才不会每次运行都重新创建
  /**
   * It's important we evaluate this lazily:
   * - We need first render to contain the DOM element, so it shouldn't happen
   *   in useEffect. We would normally put this in the constructor().
   * - We can't do 'const rootElemRef = useRef(document.createElement('div))',
   *   since this will run every single render (that's a lot).
   * - We want the ref to consistently point to the same DOM element and only
   *   ever run once.
   * @link https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily
   */
  const getRootElem = () => {
   
   
    if (!rootElemRef.current) {
   
   
      rootElemRef.current = document.createElement('div')
    }
    return rootElemRef.current
  }

  useEffect(() => {
   
   
    const root = rootElemRef.current
    const parentElem = document.createElement('div')
    appendToBody(parentElem)

    // 挂载到容器节点
    parentElem.appendChild(root)

    return () => {
   
   
      root.remove()
    }
  }, [])

  return getRootElem()
}

拿到结点后,开始着手写 Portal 组件。

全局的工具类 tool.css

.fixed {
   
   
  position: fixed;
}
.z-999 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值