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>
  )
}
相关推荐
cooldream20097 分钟前
深度解析中秋节HTML5动画的实现
前端·html·html5
jump_jump2 小时前
超长定时器 long-timeout
前端·javascript·node.js
我登哥MVP3 小时前
HTML-CSS-JS-入门学习笔记
javascript·css·笔记·学习·html
Mintopia3 小时前
架构进阶 🏗 从 CRUD 升级到“大工程师视野”
前端·javascript·全栈
Mintopia3 小时前
小样本学习在 WebAI 场景中的技术应用与局限
前端·人工智能·aigc
光影少年3 小时前
vue生态都有哪些?
前端·javascript·vue.js
一只大头猿3 小时前
基于SpringBoot和Vue的超市管理系统
前端·vue.js·spring boot
用户1456775610374 小时前
告别繁琐操作!Excel合并原来可以这么轻松
前端
itslife4 小时前
vite 源码 - 创建 ws 服务
前端·javascript