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

2023-12-13 23:45:18

[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
    });
  })

文章来源:https://blog.csdn.net/qq_41996454/article/details/134848680
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。