关于antd模态框的一种封装思路

自我介绍

看官们好,我叫JetTsang,之前都是在掘金潜水来着,现在偶尔做一些内容输出吧。

引出

在React开发当中,使用到Modal模态框的时候,通常需要去维护1个state,同时在对应的回调函数当中更改这个state来控制显隐,
比如

import React, { useState } from 'react';
import { Button, Modal } from 'antd';
const App: React.FC = () => {
    const [isModalOpen, setIsModalOpen] = useState(false); 
    const showModal = () => { setIsModalOpen(true); };
    const handleOk = () => { setIsModalOpen(false); };
    const handleCancel = () => { setIsModalOpen(false); };
    return (
    <> 
        <Button type="primary" onClick={showModal}> 
            Open Modal 
        </Button> 
        <Modal title="Basic Modal" 
            open={isModalOpen}
            onOk={handleOk}
            onCancel={handleCancel}
        >
            <p>Some contents...</p>
        </Modal> 
    </> 
    ); 
}; 
export default App;

那么是否能实现不需维护这个状态来打开Modal呢?

比如可以这样去用,用一个openModal方法来实现打开modal

import React, { useState } from 'react'; 
import { Button, Modal } from 'antd';
const App: React.FC = () => {

    const showModal = () => { setIsModalOpen(true); };
    const handleOk = () => { setIsModalOpen(false); };
    const handleCancel = () => { setIsModalOpen(false); };
    return (
        <> 
            <Button type="primary" onClick={()=>{
                openModal(
                    <Modal title="Basic Modal" 
                        onOk={handleOk}
                        onCancel={handleCancel}
                    >
                        <p>Some contents...</p>
                    </Modal> 
                )
            }}> 
                Open Modal 
            </Button> 
        </> 
    ); 
}; 
export default App;

思路

可以利用render方法,将模态框渲染到1个容器里,再利用DOM操作,把这个渲染好的容器添加到<body></body>

在React18当中

import ReactDOM from 'react-dom/client';

// 用一个fragment去做渲染容器
const container = document.createDocumentFragment();

// 将渲染容器转成 ReactDOMClient.Root 类型
const myModal = ReactDOMClient.createRoot(container)

// 调用rener方法,渲染
myModal.render(/* React.ReactElement */)

// 将渲染好的container,放入到body当中
document.body.appendChild(container);

18版本以前:

const container = document.createDocumentFragment();

// 对比18版本只是少了一个创建 ReactDOMClient.Root 的步骤

// 使用render方法,传入两个参数
ReactDOM.render(/* React.ReactElement类型 */, container);

实现

export default function OpenModal(Modal: React.ReactElement) {
    const container = document.createDocumentFragment();
    const myModal = ReactDOMClient.createRoot(container);
    
    // 克隆的时候,顺便复写props
    const cloneModal = React.cloneElement(Modal, {
        // 设置为true
        open: true,
        // 重写取消方法
        onCancel(e) {
            // ⚠️这样强行卸载会很生硬,动画效果会没有,因此不采用

            // myModal.unmount()
            
            // ⚠️因此采用这种卸载方法
            
            // 拿到关闭状态
            const removeModal = React.cloneElement(cloneModal, {

                open: false,

            } as ModalProps);
            
            // 重新渲染一下关闭状态的效果
            myModal.render(removeModal);

            Modal.props.onCancel?.(e);

        }
      })

    myModal.render(cloneModal);
    document.body.appendChild(container);

}

结尾

通过利用rendercloneElement,可以实现modal打开与关闭,而不用额外去维护open状态。当然除了React,类似的方案在Vue当中也是适用的。

写在最后

如果你需要简洁的确认框,官方也有App.useApp这种语法糖方法

关于antd模态框的一种封装思路

模态框Modal、通知提醒框Notification、全局提示Message,这仨组件如果涉及到消费上下文的,官方都是推荐hooks来调用。

关于antd模态框的一种封装思路

原文链接:https://juejin.cn/post/7247376558369275963 作者:JetTsang

(0)
上一篇 2023年6月23日 上午10:20
下一篇 2023年6月23日 上午10:30

相关推荐

发表回复

登录后才能评论