From 2122586a93a6469c85d3530821c3402e996f2097 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: Thu, 27 Nov 2025 17:47:51 +0800 Subject: [PATCH 1/3] fix: merge logic --- src/index.tsx | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 18671095..8c568792 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,7 +6,9 @@ import { isDOM } from '@rc-component/util/lib/Dom/findDOMNode'; import { getShadowRoot } from '@rc-component/util/lib/Dom/shadow'; import useEvent from '@rc-component/util/lib/hooks/useEvent'; import useId from '@rc-component/util/lib/hooks/useId'; -import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; +import useLayoutEffect, { + useLayoutUpdateEffect, +} from '@rc-component/util/lib/hooks/useLayoutEffect'; import * as React from 'react'; import Popup, { type MobileConfig } from './Popup'; import type { TriggerContextProps } from './context'; @@ -33,6 +35,7 @@ export type { }; import UniqueProvider, { type UniqueProviderProps } from './UniqueProvider'; +import { useControlledState } from '@rc-component/util'; export { UniqueProvider }; export type { UniqueProviderProps }; @@ -303,12 +306,12 @@ export function generateTrigger( : null; // ============================ Open ============================ - const [internalOpen, setInternalOpen] = React.useState( + const [internalOpen, setInternalOpen] = useControlledState( defaultPopupVisible || false, + popupVisible, ); - // Render still use props as first priority - const mergedOpen = popupVisible ?? internalOpen; + const mergedOpen = internalOpen || false; // ========================== Children ========================== const child = React.useMemo(() => { @@ -321,20 +324,9 @@ export function generateTrigger( const originChildProps = child?.props || {}; - // We use effect sync here in case `popupVisible` back to `undefined` - const setMergedOpen = useEvent((nextOpen: boolean) => { - if (openUncontrolled) { - setInternalOpen(nextOpen); - } - }); - // Support ref const isOpen = useEvent(() => mergedOpen); - useLayoutEffect(() => { - setInternalOpen(popupVisible || false); - }, [popupVisible]); - // Extract common options for UniqueProvider const getUniqueOptions = useEvent((delay: number = 0) => ({ popup, @@ -385,7 +377,7 @@ export function generateTrigger( lastTriggerRef.current = []; const internalTriggerOpen = useEvent((nextOpen: boolean) => { - setMergedOpen(nextOpen); + setInternalOpen(nextOpen); // Enter or Pointer will both trigger open state change // We only need take one to avoid duplicated change event trigger From fa9f85544c4fd92b2c09e03e5914b2416a5a5708 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: Thu, 27 Nov 2025 17:48:50 +0800 Subject: [PATCH 2/3] test: add test case --- tests/basic.test.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/basic.test.jsx b/tests/basic.test.jsx index c42f155d..ed320816 100644 --- a/tests/basic.test.jsx +++ b/tests/basic.test.jsx @@ -371,10 +371,7 @@ describe('Trigger.Basic', () => { describe('children renderProps', () => { it('should get current open', () => { const { container } = render( - Hello!} - > + Hello!}> {({ open }) => } , ); @@ -994,6 +991,16 @@ describe('Trigger.Basic', () => { expect(document.querySelector('.rc-trigger-popup-hidden')).toBeTruthy(); }); + it('defaultPopupVisible should work', async () => { + render( + +
+ , + ); + + expect(document.querySelector('.rc-trigger-popup')).toBeTruthy(); + }); + describe('click window to hide', () => { it('should hide', async () => { const onOpenChange = jest.fn(); From a7bb6e7503d84ab5cfb4b165f648af53d0ada138 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: Thu, 27 Nov 2025 17:51:21 +0800 Subject: [PATCH 3/3] chore: clean up --- docs/examples/point.tsx | 3 ++- src/index.tsx | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/examples/point.tsx b/docs/examples/point.tsx index 839092de..bf2fa50f 100644 --- a/docs/examples/point.tsx +++ b/docs/examples/point.tsx @@ -1,7 +1,8 @@ /* eslint no-console:0 */ import React from 'react'; -import Trigger, { ActionType } from '@rc-component/trigger'; +import type { ActionType } from '@rc-component/trigger'; +import Trigger from '@rc-component/trigger'; import '../../assets/index.less'; import './point.less'; diff --git a/src/index.tsx b/src/index.tsx index 8c568792..789831d4 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,9 +6,7 @@ import { isDOM } from '@rc-component/util/lib/Dom/findDOMNode'; import { getShadowRoot } from '@rc-component/util/lib/Dom/shadow'; import useEvent from '@rc-component/util/lib/hooks/useEvent'; import useId from '@rc-component/util/lib/hooks/useId'; -import useLayoutEffect, { - useLayoutUpdateEffect, -} from '@rc-component/util/lib/hooks/useLayoutEffect'; +import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import * as React from 'react'; import Popup, { type MobileConfig } from './Popup'; import type { TriggerContextProps } from './context';