React 最佳实践:如何实现原生对话框(Portals)

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战css

React 实现对话框有两种方案html

  • 使用 UI 组件库:好比 Antd 的组件 -- Modal
  • 原生 React Portals

dialog-1.png 在实际开发中,咱们大多会选择直接使用 Antd 的对话框组件,其实 Antd 对话框的实现也是基于 React Portals 这个特性,因此了解它能够让咱们的漂浮层之路走的更宽哦:react

dialog-2.png

dialog-3.png

React Portals 是什么?

Portal 是 React 16.3 新引入的 API,React 是在内存中维护了一棵 Virtual Dom Tree ,这棵树会映射到真实的 Dom 节点,而 Portal 能够打断这种映射关系,它提供了一种将子节点渲染到存在于父组件之外的 DOM 节点的优秀的方案,一举解决了漂浮层的问题,如:Dialog、Tooltip 等。markdown

用法

ReactDOM.createPortal(child, container);
复制代码

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

须要注意,虽然 child 看似显示在另外一颗 Dom 树上,但在 Virtual Dom 中,实际上是同一棵,在下面的例子中也会看到。app

Antd 的 Dialog

按照官网文档,上手很是简单dom

import React, { useState } from 'react';
import { Modal, Button } from 'antd';

const DialogPage = () => {
  const [isModalVisible, setIsModalVisible] = useState(false);

  const showModal = () => {
    setIsModalVisible(true);
  };

  const handleOk = () => {
    setIsModalVisible(false);
  };

  const handleCancel = () => {
    setIsModalVisible(false);
  };

  return (
    <> <Button type="primary" onClick={showModal}> Open Antd Modal </Button> <Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={handleCancel} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> </>
  );
};

export default DialogPage;
复制代码

Portals 的 Dialog

在 index.html 中,建立一个 dialog 的容器元素:ide

<div id="root"></div>
<div id="dialog-root"></div>
复制代码

经过组件内部状态(visible) 控制 Dialog 的渲染:oop

{
  visible
    ? createPortal(
        <div className="portal-sample"> {children} <Button onClick={onHide}>close</Button> </div>,
        document.getElementById('dialog-root'),
      )
    : null;
}
复制代码

从 React Components 能够看出来,PortalDialog 和 DialogPage 的父子关系依然存在:post

dialog-5.png

完整代码

pages/dialog.js

import React, { useState } from 'react';
import { Button } from 'antd';
import PortalDialog from '@/components/PortalDialog';

const DialogPage = () => {
  const [isPortalVisible, setIsPortalVisible] = useState(false);

  const showPortal = () => {
    setIsPortalVisible(true);
  };

  const hidePortal = () => {
    setIsPortalVisible(false);
  };

  return (
    <> <Button style={{ marginLeft: '20px' }} onClick={showPortal}> Open Dialog(React Portals) </Button> <PortalDialog visible={isPortalVisible} onHide={hidePortal}> <div>dialog-children</div> </PortalDialog> </>
  );
};

export default DialogPage;
复制代码

components/PortalDialog/index.js

import { createPortal } from 'react-dom';
import { Button } from 'antd';
import './style.css';

const PortalDialog = (props) => {
  const { visible, children, onHide } = props;
  return visible
    ? createPortal(
        <div className="portal-sample"> {children} <Button onClick={onHide}>close</Button> </div>,
        document.getElementById('dialog-root'),
      )
    : null;
};

export default PortalDialog;
复制代码

components/PortalDialog/style.css

.portal-sample {
  position: absolute;
  padding: 20px;
  width: 500px;
  height: 300px;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background-color: #fff;
  border-radius: 10px;
  border: 1px solid #ddd;
  box-shadow: 0px 0px 20px 2px #ddd;
}
复制代码

React 最佳实践

相关文章
相关标签/搜索