在之前学vue2的时候封装过一个全局的弹窗组件,可以全局任意地方通过this调用,这次大创项目是用react技术栈,看了一下项目需求,突然发现弹窗还是比较多的,主要分为基础的弹窗以及form表单式的弹窗,如果只是无脑的去写代码,那些项目也没啥必要了。正好react和hook相结合,去实现一个全局的弹窗组件,便于之后的使用。
antd组件的弹窗一般是和我们的代码放一起的,这样就导致复用性比较低,而且也显得代码比较乱。由此我就想过自己封装一个,有了之前使用vue封装的经验,我开始着手封装,基本思路就是创建一个新的div放到页面中,手动的渲染与删除,确定和取消按钮正好对应promise的成功与失败。基本思路没有问题,但是再实行的过程中,首先遇到手动渲染挂载到页面的问题,之后又遇到逻辑放到一起,无法手动控制form表单,最后突然想清楚一点就是,逻辑可以分开,把一个功能的相同点与不同点进行分离,逻辑上要单纯,最后再整合到一起。这样的话可以专注于具体的逻辑功能及实现。
封装的弹窗具体功能,其中根据类型的不同会用到form的高阶组件
import React, { useCallback, useEffect } from "react";
import ReactDOM from "react-dom/client";
import { Button, Modal } from "antd";
import { useState } from "react";
import { useForm } from "./form";
type PromiseType = {
resolve?: any;
reject?: any;
};
// modal类型(分为普通或者表单形式)
type modalType = "nomal" | "form";
/*
成功之后的回调函数
显示标题
提示文字(用于普通类型文本提示)
成功文字
配置对象(字段名,规则,默认值)
*/
type modalPropsType = {
type?: modalType;
title?: string;
infoTxt?: string;
okTxt?: string;
successCallback?: (values?: any) => void;
formOptions?: any;
};
export const useModal = (props: modalPropsType = {}) => {
const {
type = "nomal",
title = "提示",
infoTxt = "这是一段提示",
okTxt = "确定",
successCallback = () => {},
formOptions = [],
} = props;
const [show, setShow] = useState(false);
const [promiseRes, setPromiseRes] = useState();
const [containerEle, setContainerEle] = useState(null);
// 节点的挂载与卸载
useEffect(() => {
if (containerEle) {
return;
}
// 创建挂载节点
const div = document.createElement("div");
div.id = "myContainer";
document.body.append(div);
setContainerEle(div);
}, [containerEle]);
// 卸载节点
const unMounted = useCallback(() => {
if (containerEle) {
document.body.removeChild(containerEle);
setContainerEle(null);
}
}, [containerEle]);
const success = useCallback(
(values: any) => {
successCallback && successCallback();
promiseRes?.resolve(type === "nomal" ? "确定" : values);
setShow(false);
unMounted();
},
[promiseRes, unMounted, successCallback, type],
);
// 取消
const cancel = useCallback(() => {
promiseRes?.reject("取消");
setShow(false);
unMounted();
}, [unMounted, promiseRes]);
// 获取包装节点
const { MyForm } = useForm({ cancel, success, okTxt, options: formOptions });
// 挂载节点
useEffect(() => {
if (!show || !containerEle) {
return;
}
const root = ReactDOM.createRoot(containerEle as HTMLElement);
// 根据类型,去判断是简单的弹窗还是form表单
root.render(
{okTxt}
,
,
]
}
getContainer={containerEle as HTMLElement}
>
{type === "form" && }
{type === "nomal" && {infoTxt}
}
,
);
}, [
show,
MyForm,
cancel,
containerEle,
title,
infoTxt,
okTxt,
success,
type,
]);
// 初始化
const init = () => {
setShow(true);
return new Promise((resolve, reject) => {
setPromiseRes({ resolve, reject });
});
};
return { init };
};
封装的form表单(待完善)
import { Button, Form, FormInstance, Input, Space } from "antd";
import React from "react";
import { useCallback } from "react";
/*
传递配置对象()
1. 成功回调
2.失败回调
3.配置对象(自动生成form表单)
*/
type formProp = {
success: (values: any) => void;
cancel: () => void;
okTxt: string;
options?: any;
};
type FieldType = {
username?: string;
password?: string;
remember?: string;
};
export const useForm = (formProp: formProp) => {
const { success, cancel, okTxt } = formProp;
const MyForm = () => {
const formRef = React.useRef(null);
const onFinish = useCallback((values: any) => {
console.log(values);
success(values);
}, []);
const onFinishFailed = useCallback((values: any) => {
console.log(values);
}, []);
const onReset = () => {
formRef.current?.resetFields();
};
return (
label="Username"
name="username"
rules={[{ required: true, message: "Please input your username!" }]}
>
label="Password"
name="password"
rules={[{ required: true, message: "Please input your password!" }]}
>
);
};
return {
MyForm,
};
};
//可以传递type来指定类型
const nomalMadal=useModal()
//执行该函数开启弹窗
const show=()=>{
nomalMadal.init()
.then((res) => {
console.log("确定", res);
})
.catch((err) => {
console.log("取消", err);
});
}
在之后的学习过程中,要多换思路,不必拘谨于一个点,要把思维发散,逻辑可以多种方法实现,还有就是源码的能力,之后要多学一下源码,了解源码的思想还有实现方法,这样才能更好的玩转第三方库,如果只是简单的使用,那一个小白,培训个几个月也能达到使用的程度,要有自己的见解和自己的优势。