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 最佳實踐

相關文章
相關標籤/搜索