当前位置: 首页 > article >正文

[React]基于Antd的FormModal的组件封装以及useFormModal的hooks封装

[React]基于Antd的FormModal的组件封装以及useFormModal的hooks封装

场景

很常见,打开弹窗输入表单等…

封装后,弹窗自行挂载到body上,只需关注表达逻辑和打开关闭逻辑,其它的已经帮你管理好了

源码

import React, { useRef, useMemo, memo, forwardRef, useCallback, useState, useImperativeHandle, useEffect } from 'react';
import { Modal, Form } from 'antd';
import type { ModalProps } from 'antd';
import { createPortal, render, unmountComponentAtNode } from 'react-dom';

export const MyModal = memo(forwardRef((props: any, ref) => {
  useEffect(() => {
    console.log('modal had mounted')
  }, [])
  const [form] = Form.useForm();
  const [modalChildren, setModalChildren] = useState<React.ReactElement | null>(null);
  const [modalProps, setModalProps] = useState<ModalProps>({
    visible: false,
    ...(props ?? {})
  });
  const typeRef = useRef<string>();

  const onFinish = useCallback((values: any) => {
    modalProps.onOk?.(values);
  }, [form, modalProps]);

  const onClose = useCallback(() => {
    if (typeRef.current === 'form') {
      form.resetFields();
    }
    setModalProps((source) => ({
      ...source,
      visible: false,
    }));
  }, [form]);

  const onOpen = useCallback(() => {
    setModalProps((source) => ({
      ...source,
      visible: true,
    }));
  }, [form]);

  useImperativeHandle(ref, () => ({
    injectChildren: (element) => {
      setModalChildren(element);
    },
    injectModalProps: (props) => {
      console.log(props)
      setModalProps((source) => {
        return {
          ...source,
          ...props,
        }
      });
    },
    open: () => {
      onOpen();
    },
    close: () => {
      onClose();
    },
    setFieldsValue: (values: any) => {
      form.setFieldsValue?.(values);
    },
    setType: (type: string) => {
      typeRef.current = type;
    }
  }), []);

  const handleOk = useCallback((e: any) => {
    if (typeRef.current === 'form') {
      form.submit();
    } else {
      modalProps.onOk?.(e);
    }
  }, [form, modalProps]);

  return (
    <Modal
      {...modalProps}
      onCancel={onClose}
      onOk={handleOk}
    >
      {
        modalChildren
          ? React.cloneElement(modalChildren, typeRef.current === 'form'
            ? {
                onFinish,
                form,
                onClose,
              }
            : { onClose })
          : null
      }
    </Modal>
  )
}));

interface modalRefType {
  open: () => void;
  close: () => void;
  injectChildren: (child: React.ReactElement) => void;
  injectModalProps: (props: ModalProps) => void;
  setFieldsValue: (values: any) => void;
  setType: (type: string) => void;
}

interface openArgType extends ModalProps {
  children?: React.ReactElement,
  type?: 'form' | 'default',
  initialValues?: {
    [key: string]: any;
  },
}

const useMyModal = () => {
  const modalRef = useRef<modalRefType>();
  const handle = useMemo(() => {
    return {
      open: ({ children, type, initialValues, ...rest }: openArgType) => {
        console.log('modalRef.current: ', modalRef.current);
        modalRef.current?.setType(type ?? '');
        modalRef.current?.injectChildren(children ?? <div>111</div>);
        modalRef.current?.injectModalProps(rest);
        modalRef.current?.open();
        if (initialValues && type === 'form') {
          modalRef.current?.setFieldsValue?.(initialValues);
        }
      },
      close: () => {
        modalRef.current?.close();
      }
    };
  }, []);

  const containerRef = useRef<any>(document.createDocumentFragment())
  useEffect(() => {
    render(createPortal(<MyModal key="my-modal" ref={modalRef} />, document.body) as any, containerRef.current)

    return () => {
      unmountComponentAtNode(containerRef.current)
    }
  }, [])

  return [handle] as const;
}

export default useMyModal

使用Demo

const [modalHandler] = useMyModal()

// 表单组件内容
const AvailabilityForm = (props) => {
  return (
    <Form
      name="availability_form"
      {
      ...props
      }
    >
      <Form.Item
        name="time"
        rules={REQUIRED_RULE}
      >
        <DatePicker.RangePicker className='w-full' />
      </Form.Item>
    </Form>
  )
}

// 点击发布  
const handlePublish = useMemoizedFn(async () => {
    modalHandler.open({
      title: 'Available Date',
      type: 'form',
      initialValues: {},
      children: <AvailabilityForm></AvailabilityForm>,
      onOk: async (values: any) => {
        console.log('form vals', values);
        const timeRes: any[] = []

        console.log(Object.entries(values))
        Object.entries(values).forEach((item: any) => {
          const timeVal: any[] = item[1]
          if (Array.isArray(timeVal) && timeVal.length === 2) {
            timeRes.push({
              startTime: dayjs(timeVal[0]).utc().valueOf(),
              endTime: dayjs(timeVal[1]).utc().valueOf(),
            })
          }
        })

        try {
          const res = await netPublishListing(listingId, {
            availability: timeRes
          })

          if (res) {
            message.success('Success to publish')
            fetchItem()
            modalHandler.close();
          }
          console.log(res)
        } catch (err) {
          console.log('err: ', err);
        }
      },
      destroyOnClose: true,
      maskClosable: false
    });
  })

http://www.kler.cn/a/160729.html

相关文章:

  • 《深度解析 C++中的弱引用(weak reference):打破循环依赖的利器》
  • 灰狼优化算法
  • 实时渲染技术如何助力3D虚拟展厅?
  • Autosar CP DDS规范导读
  • 如何为电子课程创造创意
  • 超市里的货物架调整(算法解析)|豆包MarsCode AI刷题
  • 动态规划学习——最长回文子序列,让字符串变成回文串的最小插入次数
  • swiper和video实现抖音刷视频功能
  • P1005 [NOIP2007 提高组] 矩阵取数游戏
  • 内衣洗衣机哪个牌子好用?家用小型洗衣机推荐
  • 296_C++_一个dialog对话框在执行exec向系统发送一个延后销毁事件时,另一个对话框立刻接管了上一个对话框的销毁事件,导致死UI
  • 定时器的使用及实现
  • MySQL - 并发控制与事务的隔离级别
  • 微服务实战系列之Redis
  • 安卓发布小技巧
  • 一键AI智能改写,一键AI智能生成原创文章
  • 网络安全(一)--网络环境构成,系统的安全
  • 外包干了3个月,技术倒退2年。。。
  • 修改错误的代码和改正错误的人生一样重要
  • 智能成绩表 - 华为OD统一考试(C卷)
  • 台灯应该买什么样的才能护眼?学生护眼必备护眼台灯推荐
  • 【毕业设计】基于雷达与深度学习的摔倒检测——微多普勒效应
  • Linux虚假唤醒
  • Unity传送门特效: The Beautiful Portal/Level up/Teleport/Warp VFX
  • 网络安全缓冲区溢出实验
  • Linux C语言 37- 进程间通信IPC