@@ -193,6 +193,7 @@ export function generateTrigger(
193193 } = props ;
194194
195195 const mergedAutoDestroy = autoDestroy || false ;
196+ const openUncontrolled = popupVisible === undefined ;
196197
197198 // =========================== Mobile ===========================
198199 const isMobile = ! ! mobile ;
@@ -289,7 +290,7 @@ export function generateTrigger(
289290
290291 // We use effect sync here in case `popupVisible` back to `undefined`
291292 const setMergedOpen = useEvent ( ( nextOpen : boolean ) => {
292- if ( popupVisible === undefined ) {
293+ if ( openUncontrolled ) {
293294 setInternalOpen ( nextOpen ) ;
294295 }
295296 } ) ;
@@ -298,6 +299,38 @@ export function generateTrigger(
298299 setInternalOpen ( popupVisible || false ) ;
299300 } , [ popupVisible ] ) ;
300301
302+ // Extract common options for UniqueProvider
303+ const getUniqueOptions = useEvent ( ( delay : number = 0 ) => ( {
304+ popup,
305+ target : targetEle ,
306+ delay,
307+ prefixCls,
308+ popupClassName,
309+ popupStyle,
310+ popupPlacement,
311+ builtinPlacements,
312+ popupAlign,
313+ zIndex,
314+ mask,
315+ maskClosable,
316+ popupMotion,
317+ maskMotion,
318+ arrow,
319+ getPopupContainer,
320+ } ) ) ;
321+
322+ // Handle controlled state changes for UniqueProvider
323+ // Only sync to UniqueProvider when it's controlled mode
324+ useLayoutEffect ( ( ) => {
325+ if ( uniqueContext && targetEle && ! openUncontrolled ) {
326+ if ( mergedOpen ) {
327+ uniqueContext . show ( getUniqueOptions ( 0 ) ) ;
328+ } else {
329+ uniqueContext . hide ( 0 ) ;
330+ }
331+ }
332+ } , [ mergedOpen ] ) ;
333+
301334 const openRef = React . useRef ( mergedOpen ) ;
302335 openRef . current = mergedOpen ;
303336
@@ -324,27 +357,19 @@ export function generateTrigger(
324357 const delayInvoke = useDelay ( ) ;
325358
326359 const triggerOpen = ( nextOpen : boolean , delay = 0 ) => {
327- // If UniqueContext exists, pass delay to Provider instead of handling it internally
328- if ( uniqueContext ) {
329- if ( nextOpen && targetEle ) {
330- uniqueContext . show ( {
331- popup,
332- target : targetEle ,
333- delay,
334- prefixCls,
335- popupClassName,
336- popupStyle,
337- popupPlacement,
338- builtinPlacements,
339- popupAlign,
340- zIndex,
341- mask,
342- maskClosable,
343- popupMotion,
344- maskMotion,
345- arrow,
346- getPopupContainer,
347- } ) ;
360+ // If it's controlled mode, always use internal trigger logic
361+ // UniqueProvider will be synced through useLayoutEffect
362+ if ( popupVisible !== undefined ) {
363+ delayInvoke ( ( ) => {
364+ internalTriggerOpen ( nextOpen ) ;
365+ } , delay ) ;
366+ return ;
367+ }
368+
369+ // If UniqueContext exists and not controlled, pass delay to Provider instead of handling it internally
370+ if ( uniqueContext && openUncontrolled ) {
371+ if ( nextOpen ) {
372+ uniqueContext . show ( getUniqueOptions ( delay ) ) ;
348373 } else {
349374 uniqueContext . hide ( delay ) ;
350375 }
0 commit comments