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>
  )
}
相关推荐
纆兰几秒前
汇款单的完成
前端·javascript·html
酷酷的鱼5 分钟前
2026 React Native新架构核心:JSI底层原理与老架构深度对比
react native·react.js·架构
Lsx_18 分钟前
案例+图解带你遨游 Canvas 2D绘图 Fabric.js🔥🔥(5W+字)
前端·javascript·canvas
2501_9445210032 分钟前
rn_for_openharmony商城项目app实战-主题设置实现
javascript·数据库·react native·react.js·ecmascript
m0_471199631 小时前
【场景】如何快速接手一个前端项目
前端·vue.js·react.js
榴莲CC1 小时前
抗干扰LED数显屏驱动VK1624 数码管显示芯片 3线串行接口
前端
lili-felicity1 小时前
React Native for Harmony 个人消息列表最新消息置顶实现(多维度权重统计)
javascript·react native·react.js
Tigger1 小时前
用 Vue 3 做了一套年会抽奖工具,顺便踩了些坑
前端·javascript·vue.js
天天扭码1 小时前
一文搞懂——React 19到底更新了什么
前端·react.js·前端框架
weixin_462446231 小时前
【原创】使用langchain与MCP 与 Chrome DevTools 打造可调用浏览器工具的 Chat Agent
前端·langchain·chrome devtools