zoukankan      html  css  js  c++  java
  • React Portal

    对于需要使用弹出层的需求 ,Portal可以说是提供了一种完美的解决方案。相比于React Native中的实现更多的使用Modal或者绝对定位,Portal实在是简易友好得多。

    场景

    对话框,确认提示框,悬浮窗这些组件,一般都要做一个比当前视图层层级更高的View,但是现有的方案都很难跳出父容器的约束。如何破除这个约束?

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

    子节点可以被渲染到父组件之外的DOM,这不就是解除了这个约束了吗?

    Portal:传送门。steam上有一款名为 Portal2 的游戏,互相利用传送门进行位移的配合闯关游戏,传送的含义和这里颇为相似,不过这里是“传送”组件。

    使用和效果

    普通的 render 方法就是将组件内的元素挂载到最近的DOM节点中。而 Portal 则通过一个API,将元素挂载到任意有效的DOM节点上。

    ReactDOM.createPortal( children,container,key? )

    具体参数类型如下:

    现在开始在代码中使用:

    1. public目录下的index.html文件中,设置<div id="modal"></div>。后面 React 元素会被放到modal这个div里,这个DOM元素就是container

        <div id="root"></div>
        <div id="modal"></div>
      
    2. 对该 API 做简单封装,构建Modal组件。因为Portal API是读children子组件的,所以Portal肯定是作为容器来用。

      (这里直接操作操作了children,你也可以像Portal文档一样,在其中多加一个div层级隔离)

      const modalDivElement = document.getElementById("modal");
      
      export default function Modal({ children }) {
        return ReactDOM.createPortal(children, modalDivElement);
      }
      

      使用createPortal API,Modal 组件中的子元素children会被渲染到modalDivElement中,而不管你的Modal在何处使用。

    3. 使用Modal组件。在红色背景div中嵌入了Modal, Modal中只有一个div字符串zhangsan

      export default function App() {
        return (
          <div style={{ backgroundColor: "#a00",  `200px`, height: `100px` }}>
            <Modal>
              <div>zhangsan</div>
            </Modal>
          </div>
        );
      }
      

      现在来看看效果:

      如果没有Modal层级,zhanghsan是应该在红色区域里面的。但是为什么加了Modal就在外面了?这时检查渲染结构,秘密就在这里:

      可以看到,Modal中的元素被渲染到了id="modal"的这个div里。来回顾一下发生了什么,我们的代码组件结构是这样:

            <div id="root">
              <div style={{ backgroundColor: "#a00",  `200px`, height: `100px` }}>
                <Modal>
                  <div>zhangsan</div>
                </Modal>
              </div>
            </div>
            <div id="modal"></div>
      

      但是实际的渲染结构却是这样(观察其中Modal子元素的变化):

            <div id="root">
              <div style={{ backgroundColor: "#a00",  `200px`, height: `100px` }</div>
            </div>
            <div id="modal">
              <div>zhangsan</div>
            </div>
      

      实际渲染结构和代码结构并不一致了 ,是因为Modal中的Portal将子元素方法放到了指定的container中。子元素被跨层级渲染,就像被传送过去一样,这便是Portal

    制作一个 Modal 弹出框组件

    上面只是演示了Portal传送子组件的效果,如果要做到弹出蒙层的效果,只需要在需要传送的children子元素上添加一个div包裹并添加样式,如果弹出层样式一致,可以直接Modal组件中,当然也可以在具体的children中实现并自定义样式。

    实例:

    例子代码:

    export default function App() {
      const [isShow, setIsShow] = useState(false);
      function click(e) {
        console.info("e", e);
        setIsShow(!isShow);
      }
      return (
        <div
          style={{
            backgroundColor: "#a00",
             `200px`,
            height: `100px`,
          }}
          onClick={click}
        >
          {isShow && (
            <Modal>
              <span>zhangsan</span>
            </Modal>
          )}
        </div>
      );
    }
    

    Modal组件:

    const modalDivElement = document.getElementById("modal");
    
    export default function Modal({ children }) {
      const modalContent = (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            position: "absolute",
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            backgroundColor: `rgba(0, 0, 0, 0.5)`,
          }}
        >
          {children}
        </div>
      );
      return ReactDOM.createPortal(modalContent, modalDivElement);
    }
    

    如果Modal交互和内容切换较多,可以进一步封装后通过ref命令式的调用,动态传入Modal和子组件切换。

    Portal 事件冒泡

    Portal中的事件冒泡是遵从React结构的,并不是实际渲染的DOM元素结构,也就是说上面的root节点可以获取到modal中冒泡出来的事件。

    而,上面的页面中的click事件,可以获取点击红色区域触发Modal的事件,也能获取到Modal内部的点击span的事件:

    这样,Modal中的交互控制并没有脱离当前的页面或组件,和没有使用Portal时的事件表现一致。

  • 相关阅读:
    个人作业——软件产品案例分析
    项目Beta冲刺(团队)随笔集
    【Alpha】随笔集合
    个人作业——软件工程实践总结作业
    用户调查报告
    项目Beta冲刺(团队)总结
    项目Beta冲刺(团队)第七天
    项目Beta冲刺(团队)第六天
    项目Beta冲刺(团队)第五天
    项目Beta冲刺(团队)第四天
  • 原文地址:https://www.cnblogs.com/xuxiaowei/p/14341335.html
Copyright © 2011-2022 走看看