From 992b99c01151f99e1b4858eeb8bfa47866c4964b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 2 Mar 2021 18:52:47 +0800 Subject: [PATCH] fix: Modal hooks update & destroy (#29584) * fix: Modal update before render * test: Add test case * test: More test --- components/modal/__tests__/hook.test.js | 71 +++++++++++++++++++++++-- components/modal/useModal/index.tsx | 31 ++++++++++- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/components/modal/__tests__/hook.test.js b/components/modal/__tests__/hook.test.js index 59b2b41189..1d395fedac 100644 --- a/components/modal/__tests__/hook.test.js +++ b/components/modal/__tests__/hook.test.js @@ -1,5 +1,6 @@ import React from 'react'; import CSSMotion from 'rc-motion'; +import { act } from 'react-dom/test-utils'; import { genCSSMotion } from 'rc-motion/lib/CSSMotion'; import { mount } from 'enzyme'; import Modal from '..'; @@ -48,15 +49,19 @@ describe('Modal.hook', () => { expect(wrapper.find('.ant-modal-body').length).toBeTruthy(); // Update instance - instance.update({ - content:
, + act(() => { + instance.update({ + content:
, + }); }); wrapper.update(); expect(wrapper.find('.updated-content')).toHaveLength(1); // Destroy - instance.destroy(); - jest.runAllTimers(); + act(() => { + instance.destroy(); + jest.runAllTimers(); + }); wrapper.update(); expect(wrapper.find('Modal')).toHaveLength(0); @@ -100,4 +105,62 @@ describe('Modal.hook', () => { wrapper.find('.ant-modal-wrap').simulate('click'); expect(cancelCount).toEqual(2); // click modal wrapper, trigger onCancel }); + + it('update before render', () => { + const Demo = () => { + const [modal, contextHolder] = Modal.useModal(); + + const openBrokenModal = React.useCallback(() => { + const instance = modal.info({ + title: 'Light', + }); + + instance.update({ + title: 'Bamboo', + }); + }, [modal]); + + return ( +
+ {contextHolder} +
+ Test hook modal +
+
+ ); + }; + + const wrapper = mount(); + wrapper.find('.open-hook-modal-btn').simulate('click'); + + expect(wrapper.find('.ant-modal-confirm-title').text()).toEqual('Bamboo'); + }); + + it('destroy before render', () => { + const Demo = () => { + const [modal, contextHolder] = Modal.useModal(); + + const openBrokenModal = React.useCallback(() => { + const instance = modal.info({ + title: 'Light', + }); + + instance.destroy(); + }, [modal]); + + return ( +
+ {contextHolder} +
+ Test hook modal +
+
+ ); + }; + + const wrapper = mount(); + wrapper.find('.open-hook-modal-btn').simulate('click'); + + expect(wrapper.exists('.ant-modal-confirm-title')).toBeFalsy(); + }); }); diff --git a/components/modal/useModal/index.tsx b/components/modal/useModal/index.tsx index baf892e654..7408989711 100644 --- a/components/modal/useModal/index.tsx +++ b/components/modal/useModal/index.tsx @@ -34,6 +34,21 @@ const ElementsHolder = React.memo( export default function useModal(): [Omit, React.ReactElement] { const holderRef = React.useRef(null as any); + // ========================== Effect ========================== + const [actionQueue, setActionQueue] = React.useState<(() => void)[]>([]); + + React.useEffect(() => { + if (actionQueue.length) { + const cloneQueue = [...actionQueue]; + cloneQueue.forEach(action => { + action(); + }); + + setActionQueue([]); + } + }, [actionQueue]); + + // =========================== Hook =========================== const getConfirmFunc = React.useCallback( (withFunc: (config: ModalFuncProps) => ModalFuncProps) => function hookConfirm(config: ModalFuncProps) { @@ -57,13 +72,25 @@ export default function useModal(): [Omit, React.R return { destroy: () => { + function destroyAction() { + modalRef.current?.destroy(); + } + if (modalRef.current) { - modalRef.current.destroy(); + destroyAction(); + } else { + setActionQueue(prev => [...prev, destroyAction]); } }, update: (newConfig: ModalFuncProps) => { + function updateAction() { + modalRef.current?.update(newConfig); + } + if (modalRef.current) { - modalRef.current.update(newConfig); + updateAction(); + } else { + setActionQueue(prev => [...prev, updateAction]); } }, };