From 349ea900b2f368b0a81ecdef6584c4d5d27c5dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 20 Jul 2026 15:09:00 +0800 Subject: [PATCH 1/4] fix: destroyOnHidden should work when forceRender is true - Fix logic to use animatedVisible to check if component has ever been opened - Add test case for forceRender + destroyOnHidden scenario Co-Authored-By: Claude --- src/DialogWrap.tsx | 3 ++- tests/index.spec.tsx | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 76563968..2467423f 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -46,7 +46,8 @@ const DialogWrap: React.FC = (props) => { }, [visible]); // Destroy on close will remove wrapped div - if (!forceRender && destroyOnHidden && !animatedVisible) { + // Use `animatedVisible` to check if component has ever been opened + if (destroyOnHidden && animatedVisible && !visible) { return null; } diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index dcb33153..438a4a77 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -158,6 +158,32 @@ describe('dialog', () => { expect(document.querySelector('.test-input')).toHaveValue(''); }); + + it('should destroy even when forceRender is true', () => { + const Demo: React.FC> = (props) => ( + + + + ); + + const { rerender } = render(); + + // Force render, but not visible yet - should still render (forceRender) + expect(document.querySelectorAll('.test-force-destroy')).toHaveLength(1); + + // Show + rerender(); + act(() => { + jest.runAllTimers(); + }); + + // Hide - should destroy because destroyOnHidden is true + rerender(); + act(() => { + jest.runAllTimers(); + }); + expect(document.querySelectorAll('.test-force-destroy')).toHaveLength(0); + }); }); it('esc to close', () => { From 28ecb13ad5c2b9cddbf83542671da117a2ca7d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 20 Jul 2026 16:34:43 +0800 Subject: [PATCH 2/4] fix: destroyOnHidden should work with forceRender - Use hasOpenedRef to track if dialog has ever been opened - Wait for afterClose callback before destroying (preserves animation) - Fixes regression where dialog would unmount immediately without animation --- src/DialogWrap.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 2467423f..69814d38 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -28,6 +28,7 @@ const DialogWrap: React.FC = (props) => { } = props; const { scrollLock: _, ...restProps } = props; const [animatedVisible, setAnimatedVisible] = React.useState(visible); + const hasOpenedRef = React.useRef(false); const refContext = React.useMemo(() => ({ panel: panelRef }), [panelRef]); @@ -42,12 +43,13 @@ const DialogWrap: React.FC = (props) => { React.useEffect(() => { if (visible) { setAnimatedVisible(true); + hasOpenedRef.current = true; } }, [visible]); // Destroy on close will remove wrapped div - // Use `animatedVisible` to check if component has ever been opened - if (destroyOnHidden && animatedVisible && !visible) { + // Use `hasOpenedRef` to check if component has ever been opened (after animation completes) + if (destroyOnHidden && hasOpenedRef.current && !visible) { return null; } From fc188afc635d4478e0a3257421158dca06e17441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 20 Jul 2026 16:58:59 +0800 Subject: [PATCH 3/4] fix: destroyOnHidden should wait for animation to complete - Add !animatedVisible to condition to ensure exit animation plays - afterClose callback will be called before destroying component --- src/DialogWrap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 69814d38..6923c2df 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -49,7 +49,7 @@ const DialogWrap: React.FC = (props) => { // Destroy on close will remove wrapped div // Use `hasOpenedRef` to check if component has ever been opened (after animation completes) - if (destroyOnHidden && hasOpenedRef.current && !visible) { + if (destroyOnHidden && hasOpenedRef.current && !visible && !animatedVisible) { return null; } From b23af5ba78d38283e69f01778c513470e8afaa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 20 Jul 2026 17:07:32 +0800 Subject: [PATCH 4/4] fix: destroyOnHidden should work with forceRender - Use hasOpenedRef initialized with visible prop to track opened state - Add !animatedVisible to wait for animation before destroy - Fixes exit animation being interrupted --- src/DialogWrap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 6923c2df..cac6b48e 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -28,7 +28,7 @@ const DialogWrap: React.FC = (props) => { } = props; const { scrollLock: _, ...restProps } = props; const [animatedVisible, setAnimatedVisible] = React.useState(visible); - const hasOpenedRef = React.useRef(false); + const hasOpenedRef = React.useRef(visible); const refContext = React.useMemo(() => ({ panel: panelRef }), [panelRef]);