React 封装命令式弹窗

概要: 使用 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>
  )
}
相关推荐
hfd19901 分钟前
Chrome 插件开发实战:从入门到进阶
前端·chrome
菠萝+冰3 分钟前
CSS 定位的核心属性:position
前端·css
文艺理科生17 分钟前
Nuxt 状态管理权威指南:从 useState 到 Pinia
前端·javascript·vue.js
汪子熙21 分钟前
解决 Node.js 无法获取本地颁发者证书问题的详细分析与代码示例
javascript·后端
Cache技术分享29 分钟前
169. Java Lambda 表达式 - 使用自然顺序比较对象
前端·后端
心宽路阔走天下38 分钟前
html页面打水印效果
前端·css·html
piaoyunlive1 小时前
Base64 编码优化 Web 图片加载:异步响应式架构(Java 后端 + 前端全流程实现)
java·前端·架构
wow_DG1 小时前
【React ✨】从零搭建 React 项目:脚手架与工程化实战(2025 版)
前端·react.js·前端框架
秋秋小事1 小时前
React Hooks UseRef的用法
前端·javascript·react.js
yinuo1 小时前
uniapp换肤最佳实践
前端