index.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import React, {
  2. useRef,
  3. useMemo,
  4. memo,
  5. forwardRef,
  6. useCallback,
  7. useState,
  8. useImperativeHandle,
  9. useId,
  10. useTransition
  11. } from 'react'
  12. import { Modal, Form } from 'antd'
  13. import type { ModalProps } from 'antd'
  14. const MyModal = memo(
  15. forwardRef((prop: any, ref) => {
  16. const [, startTransition] = useTransition()
  17. const [form] = Form.useForm()
  18. const [modalChildren, setModalChildren] = useState<React.ReactElement>(null)
  19. const [loading, setLoading] = useState(false)
  20. const [modalProps, setModalProps] = useState<ModalProps>({
  21. open: false
  22. })
  23. const typeRef = useRef<string>()
  24. const onFinish = useCallback(
  25. (values: any) => {
  26. const response = modalProps.onOk?.(values)
  27. if (response instanceof Promise) {
  28. setLoading(true)
  29. response.finally(() => {
  30. setLoading(false)
  31. })
  32. }
  33. },
  34. [form, modalProps]
  35. )
  36. // ant.design 4.0 Form的onFinish触发回调
  37. // const onFinish = useCallback(
  38. // (values: any) => {
  39. // modalProps.onOk?.(values)
  40. // },
  41. // [form, modalProps]
  42. // )
  43. // 关闭当前Modal
  44. const onClose = useCallback(() => {
  45. setModalProps(source => ({
  46. ...source,
  47. open: false
  48. }))
  49. startTransition(() => {
  50. if (typeRef.current === 'form') {
  51. form.resetFields()
  52. }
  53. })
  54. }, [form])
  55. // 打开当前Modal
  56. const onOpen = useCallback(() => {
  57. setModalProps(source => ({
  58. ...source,
  59. open: true
  60. }))
  61. }, [form])
  62. useImperativeHandle(
  63. ref,
  64. () => ({
  65. // 注入Modal的子组件
  66. injectChildren: element => {
  67. setModalChildren(element)
  68. },
  69. // 注入Modal参数
  70. injectModalProps: props => {
  71. setModalProps(source => {
  72. return {
  73. ...source,
  74. ...props
  75. }
  76. })
  77. },
  78. // 打开Modal
  79. open: () => {
  80. onOpen()
  81. },
  82. // 关闭Modal
  83. close: () => {
  84. if (loading) setLoading(false)
  85. onClose()
  86. },
  87. // 设置表单数据
  88. setFieldsValue: (values: any) => {
  89. form.setFieldsValue?.(values)
  90. },
  91. setType: (type: string) => {
  92. typeRef.current = type
  93. }
  94. }),
  95. []
  96. )
  97. const handleOk = useCallback(() => {
  98. if (typeRef.current === 'form') {
  99. form.submit()
  100. } else {
  101. modalProps.onOk?.(null)
  102. }
  103. }, [form, modalProps])
  104. // 这里的Modal是ant.design中的Modal
  105. return (
  106. <Modal {...modalProps} onCancel={onClose} onOk={handleOk} okButtonProps={{ loading }} destroyOnClose>
  107. {modalChildren
  108. ? React.cloneElement(modalChildren, {
  109. onFinish,
  110. form,
  111. onClose
  112. })
  113. : null}
  114. </Modal>
  115. )
  116. })
  117. )
  118. interface modalRefType {
  119. open: () => void
  120. close: () => void
  121. injectChildren: (child: React.ReactElement) => void
  122. injectModalProps: (props: ModalProps) => void
  123. setFieldsValue: (values: any) => void
  124. setType: (type: string) => void
  125. }
  126. interface OpenArgType extends ModalProps {
  127. children?: React.ReactElement
  128. type?: 'form' | 'default'
  129. initialValues?: {
  130. [key: string]: any
  131. }
  132. }
  133. /**
  134. * 适用于单弹窗模式
  135. */
  136. const useModal = () => {
  137. const modalRef = useRef<modalRefType>()
  138. const handle = useMemo(() => {
  139. return {
  140. open: ({ children, type, initialValues, ...rest }: OpenArgType) => {
  141. modalRef.current.setType(type)
  142. modalRef.current.injectChildren(children) // 注入子组件
  143. modalRef.current.injectModalProps(rest) // 注入Modal的参数
  144. modalRef.current.open()
  145. if (initialValues && type === 'form') {
  146. modalRef.current.setFieldsValue?.(initialValues)
  147. }
  148. },
  149. close: () => {
  150. modalRef.current.close()
  151. }
  152. }
  153. }, [])
  154. return [handle, <MyModal ref={modalRef} key={useId()} />] as const
  155. }
  156. export type ModalAction = {
  157. open: (args: OpenArgType) => void
  158. close: () => void
  159. }
  160. export default useModal