[ffxixslh] React - 学习笔记 - Portals

React Portal允许你将组件渲染到父组件之外的指定DOM节点,常用于创建对话框、模态框等需要‘跳出’容器的元素。本文详细介绍了Portal的使用方法,包括创建、事件冒泡及应用场景,展示了如何在有overflow:hidden限制的父组件中,依然使子组件能够捕获和响应事件。
摘要由CSDN通过智能技术生成

Portals

portal 可以将子节点渲染到父组件之外的其他 DOM 节点中。

语法:

ReactDom.createPortal(child, container)

用法:

通常来说,组件 render 返回的元素会被挂载到 DOM 节点中离其父组件最近的节点;有时候,将子元素插入到 DOM 节点中 的不同位置也是有好处的:

render() {
    // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
    // `domNode` 是一个可以在任何位置的有效 DOM 节点。
    return ReactDOM.createPortal(
        this.props.children,
        domNode,
    )
}

应用

一个 portal 的典型用例是当父组件有 overflow: hiddenz-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框悬浮卡以及提示框等:

<style>
  #root {
    overflow: hidden;
  }
</style>

<div id="root"></div>
<div id="modal"></div>
import React, { useState, useEffect } from "react";
import { createPortal } from "react-dom";

// 1. 获取 DOM 的根节点
const rootEl = document.getElementById("root");
const modalRootEl = document.getElementById("modal");

const Modal = ({ children }) => {
  const el = document.createElement("div");
  useEffect(() => {
    // 2. 在 Modal 挂载时添加 el 节点
    modalRootEl.appendChild(el);
    return () => {
      // 在卸载时移除 el 节点
      modalRootEl.removeChild(el);
    };
  }, []);
  // 3. 将 children 属性挂载到 el 节点
  return createPortal(children, el);
};

const modalStyle = {
  backgroundColor: "rgba(0, 0, 0, 0.5)",
  position: "fixed",
  width: "100%",
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  top: 0,
  left: 0,
};

const Portals = () => {
  const [showModal, setShowModal] = useState(false);
  const handleShow = () => {
    setShowModal(true);
  };
  const handleHide = () => {
    setShowModal(false);
  };

  const modal = showModal ? (
    <Modal>
      <div style={modalStyle}>
        <div>With a portal, we can render content into a different</div>
        <button onClick={handleHide}>hide modal</button>
      </div>
    </Modal>
  ) : null;

  return (
    <div>
      This div has overflow:hidden.
      <button onClick={handleShow}>show modal</button>
      {modal}
    </div>
  );
};

export default Portals;

在这里插入图片描述

在这里插入图片描述

通过 Portal 进行事件冒泡

DOM 中 modal 的兄弟节点 root 可以捕获来自 Protal 冒泡的事件:

import React, { useState, useEffect, memo } from "react";
import { Component } from "react";
import { createPortal } from "react-dom";

const rootEl = document.getElementById("root");
const modalRootEl = document.getElementById("modal");

const Modal = ({ children }) => {
  const el = document.createElement("div");

  useEffect(() => {
    modalRootEl.appendChild(el);
    return () => {
      modalRootEl.removeChild(el);
    };
  });

  return createPortal(children, el);
};

const modalStyle = {
  backgroundColor: "rgba(0, 0, 0, 0.5)",
  position: "fixed",
  width: "100%",
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  top: 0,
  left: 0,
};

const Portals = () => {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount((count) => count + 1);
  };

  return (
    <div onClick={handleClick}>
      <div>count:{count}</div>
      Open up the browser DevTools to observe that the button is not a child of
      the div with the onClick handler.
      <Modal>
        <Child />
      </Modal>
    </div>
  );
};

const Child = () => {
  return (
    <div style={modalStyle}>
      <button>click</button>
    </div>
  );
};
export default Portals;

在这里插入图片描述

在父组件里捕获一个来自 portal 冒泡上来的事件,使之能够在开发时具有不完全依赖于 portal 的更为灵活的抽象。例如,如果你在渲染一个 <Modal /> 组件,无论其是否采用 portal 实现,父组件都能够捕获其事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值