概要: 使用 React 封装一个命令式弹窗组件, 弹窗中包含一个自定义的表单组件, 实现表单数据搜集功能.
效果图:

底部的按钮属于弹窗组件, 点击确定可以搜集表单中输入的数据.
React 以及相关组件的版本
"@ant-design/icons": "^5.6.1",
"@ant-design/v5-patch-for-react-19": "^1.0.3",
"antd": "^5.27.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.8.1",
1. withForm.js
定义一个高阶函数, 传入表单组件, 返回一个新组件
import { Modal } from "antd";
import { useState, useRef, useEffect } from "react";
// 将表单组件作为入参
const withForm = (FormCmp) => {
const CmpWithForm = ({ visible, title, okText, okTrigger, cancelTrigger, afterClose }) => {
const [isOpen, setIsOpen] = useState(visible);
const formRef = useRef(null);
const okHandler = () => {
setIsOpen(false);
// 获取表单数据
const formData = formRef.current.getFormData();
if (okTrigger) {
// 将表单数据传给外部的处理函数
okTrigger(formData);
}
}
const cancelHandler = () => {
setIsOpen(false);
if (cancelTrigger) {
cancelTrigger();
}
}
useEffect(() => {
if (!isOpen) {
// 关闭后销毁dom
afterClose();
}
}, [isOpen])
return (
<Modal
title={title}
open={isOpen}
onOk={okHandler}
onCancel={cancelHandler}
okText={okText}
maskClosable={false}
>
<div style={{ marginTop: '40px', marginRight: '40px' }}>
<FormCmp ref={formRef} />
</div>
</Modal>
)
}
return CmpWithForm;
}
export default withForm;
2. createModalWithForm.js
使用 render 函数手动渲染组件, 实现"命令式"调用的功能
import { createRoot } from 'react-dom/client';
import { createElement } from "react";
import withForm from './withForm';
const createModalWithForm = (formCmp, { title, okText, okTrigger }) => {
// 销毁组件
const destroyModal = () => {
setTimeout(() => {
root.unmount();
container.parentNode.removeChild(container);
}, 800)
}
// 创建DOM容器
const container = document.createElement('div');
document.body.appendChild(container);
const root = createRoot(container);
const ModalCmp = withForm(formCmp);
// 创建jsx组件
const jsxCmp = createElement(ModalCmp, {
visible: true,
title: title,
okText: okText,
okTrigger: okTrigger,
afterClose: destroyModal
})
// 渲染组件
root.render(jsxCmp);
}
export default createModalWithForm;
3. UserForm.jsx
自定义的表单组件, 使用 useImperativeHandle 将表单组件中定义的函数暴露出去, 供外部的父组件调用. 具体调用的方式见 withForm.js 中 okHandler 回调函数, 使用ref.
import { useEffect, useImperativeHandle } from "react";
import { Form, Input, InputNumber } from "antd";
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const UserForm = ({ref}) => {
const [form] = Form.useForm();
console.log(ref);
// 初始化表单数据
// useEffect(() => {
// if (initialValues) {
// form.setFieldsValue(initialValues);
// }
// }, [form, initialValues]);
const getFormData = () => {
let formData = form.getFieldsValue();
return formData;
}
// 将获取表单数据的方法暴露给父组件
useImperativeHandle(ref, () => ({
getFormData
}))
return (
<Form
form={form}
{...formItemLayout}
layout="horizontal"
>
<Form.Item
name="name"
label="姓名"
rules={[
{ required: true, message: '请输入姓名' },
{ min: 2, message: '姓名至少2个字符' },
]}
>
<Input placeholder="请输入姓名" />
</Form.Item>
<Form.Item
name="age"
label="年龄"
rules={[
{ required: true, message: '请输入年龄' },
{ type: 'number', min: 0, max: 1000, message: '年龄必须在18-120之间' },
]}
>
<InputNumber placeholder="请输入年龄" style={{ width: '100%' }} />
</Form.Item>
</Form>
)
}
export default UserForm;
4. MyModalTestV5.jsx
外部的测试组件, 点击按钮弹出弹窗
import {Button} from 'antd';
import createModalWithForm from '../../../hooks/modal/v5/createModalWithForm';
import UserForm from '../../../hooks/modal/v5/UserForm';
export default function MyModalTestV5() {
const showModal = () => {
createModalWithForm(UserForm, {
title: "新增用户",
okText: "确定",
okTrigger: okTrigger,
})
}
// 点击确定按钮, 拿到表单数据, 可进行表单提交等操作!!!
const okTrigger = (formData) => {
console.log("MyModalTestV5 ", formData);
}
return (
<div>
<Button type="primary" onClick={showModal}>点我弹框 --V5</Button>
</div>
)
}