1313import  { AriaLabelingProps ,  BaseEvent ,  DOMProps ,  FocusableElement ,  FocusEvents ,  KeyboardEvents ,  Node ,  RefObject ,  ValueBase }  from  '@react-types/shared' ; 
1414import  { AriaTextFieldProps }  from  '@react-aria/textfield' ; 
1515import  { AutocompleteProps ,  AutocompleteState }  from  '@react-stately/autocomplete' ; 
16- import  { CLEAR_FOCUS_EVENT ,  FOCUS_EVENT ,  getActiveElement ,  getOwnerDocument ,  isAndroid ,  isCtrlKeyPressed ,  isIOS ,  mergeProps ,  mergeRefs ,  useEffectEvent ,  useEvent ,  useId ,  useLabels ,  useObjectRef }  from  '@react-aria/utils' ; 
16+ import  { CLEAR_FOCUS_EVENT ,  FOCUS_EVENT ,  getActiveElement ,  getOwnerDocument ,  isAndroid ,  isCtrlKeyPressed ,  isIOS ,  mergeProps ,  mergeRefs ,  useEffectEvent ,  useEvent ,  useId ,  useLabels ,  useLayoutEffect ,   useObjectRef }  from  '@react-aria/utils' ; 
1717import  { dispatchVirtualBlur ,  dispatchVirtualFocus ,  getVirtuallyFocusedElement ,  moveVirtualFocus }  from  '@react-aria/focus' ; 
1818import  { getInteractionModality }  from  '@react-aria/interactions' ; 
1919// @ts -ignore 
@@ -92,7 +92,6 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
9292  let  timeout  =  useRef < ReturnType < typeof  setTimeout >  |  undefined > ( undefined ) ; 
9393  let  delayNextActiveDescendant  =  useRef ( false ) ; 
9494  let  queuedActiveDescendant  =  useRef < string  |  null > ( null ) ; 
95-   let  lastCollectionNode  =  useRef < HTMLElement > ( null ) ; 
9695
9796  // For mobile screen readers, we don't want virtual focus, instead opting to disable FocusScope's restoreFocus and manually 
9897  // moving focus back to the subtriggers 
@@ -106,7 +105,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
106105    return  ( )  =>  clearTimeout ( timeout . current ) ; 
107106  } ,  [ ] ) ; 
108107
109-   let  updateActiveDescendant  =  useEffectEvent ( ( e : Event )  =>  { 
108+   let  updateActiveDescendantEvent  =  useEffectEvent ( ( e : Event )  =>  { 
110109    // Ensure input is focused if the user clicks on the collection directly. 
111110    if  ( ! e . isTrusted  &&  shouldUseVirtualFocus  &&  inputRef . current  &&  getActiveElement ( getOwnerDocument ( inputRef . current ) )  !==  inputRef . current )  { 
112111      inputRef . current . focus ( ) ; 
@@ -140,32 +139,36 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
140139    delayNextActiveDescendant . current  =  false ; 
141140  } ) ; 
142141
143-   let  callbackRef  =  useCallback ( ( collectionNode )  =>  { 
144-     if  ( collectionNode  !=  null )  { 
145-       // When typing forward, we want to delay the setting of active descendant to not interrupt the native screen reader announcement 
146-       // of the letter you just typed. If we recieve another focus event then we clear the queued update 
147-       // We track lastCollectionNode to do proper cleanup since callbackRefs just pass null when unmounting. This also handles 
148-       // React 19's extra call of the callback ref in strict mode 
149-       lastCollectionNode . current ?. removeEventListener ( 'focusin' ,  updateActiveDescendant ) ; 
150-       lastCollectionNode . current  =  collectionNode ; 
151-       collectionNode . addEventListener ( 'focusin' ,  updateActiveDescendant ) ; 
142+   let  [ collectionNode ,  setCollectionNode ]  =  useState < HTMLElement  |  null > ( null ) ; 
143+   let  callbackRef  =  useCallback ( ( node )  =>  { 
144+     setCollectionNode ( node ) ; 
145+     if  ( node  !=  null )  { 
152146      // If useSelectableCollection isn't passed shouldUseVirtualFocus even when useAutocomplete provides it 
153147      // that means the collection doesn't support it (e.g. Table). If that is the case, we need to disable it here regardless 
154148      // of what the user's provided so that the input doesn't recieve the onKeyDown and autocomplete props. 
155-       if  ( collectionNode . getAttribute ( 'tabindex' )  !=  null )  { 
149+       if  ( node . getAttribute ( 'tabindex' )  !=  null )  { 
156150        setShouldUseVirtualFocus ( false ) ; 
157151      } 
158152      setHasCollection ( true ) ; 
159153    }  else  { 
160-       lastCollectionNode . current ?. removeEventListener ( 'focusin' ,  updateActiveDescendant ) ; 
161154      setHasCollection ( false ) ; 
162155    } 
163-   } ,  [ updateActiveDescendant ] ) ; 
156+   } ,  [ ] ) ; 
157+   useLayoutEffect ( ( )  =>  { 
158+     if  ( collectionNode  !=  null )  { 
159+       // When typing forward, we want to delay the setting of active descendant to not interrupt the native screen reader announcement 
160+       // of the letter you just typed. If we recieve another focus event then we clear the queued update 
161+       collectionNode . addEventListener ( 'focusin' ,  updateActiveDescendantEvent ) ; 
162+     } 
163+     return  ( )  =>  { 
164+       collectionNode ?. removeEventListener ( 'focusin' ,  updateActiveDescendantEvent ) ; 
165+     } ; 
166+   } ,  [ collectionNode ] ) ; 
164167
165168  // Make sure to memo so that React doesn't keep registering a new event listeners on every rerender of the wrapped collection 
166169  let  mergedCollectionRef  =  useObjectRef ( useMemo ( ( )  =>  mergeRefs ( collectionRef ,  callbackRef ) ,  [ collectionRef ,  callbackRef ] ) ) ; 
167170
168-   let  focusFirstItem  =  useEffectEvent ( ( )  =>  { 
171+   let  focusFirstItem  =  useCallback ( ( )  =>  { 
169172    delayNextActiveDescendant . current  =  true ; 
170173    collectionRef . current ?. dispatchEvent ( 
171174      new  CustomEvent ( FOCUS_EVENT ,  { 
@@ -176,9 +179,9 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
176179        } 
177180      } ) 
178181    ) ; 
179-   } ) ; 
182+   } ,   [ collectionRef ] ) ; 
180183
181-   let  clearVirtualFocus  =  useEffectEvent ( ( clearFocusKey ?: boolean )  =>  { 
184+   let  clearVirtualFocus  =  useCallback ( ( clearFocusKey ?: boolean )  =>  { 
182185    moveVirtualFocus ( getActiveElement ( ) ) ; 
183186    queuedActiveDescendant . current  =  null ; 
184187    state . setFocusedNodeId ( null ) ; 
@@ -192,7 +195,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
192195    clearTimeout ( timeout . current ) ; 
193196    delayNextActiveDescendant . current  =  false ; 
194197    collectionRef . current ?. dispatchEvent ( clearFocusEvent ) ; 
195-   } ) ; 
198+   } ,   [ collectionRef ,   state ] ) ; 
196199
197200  let  lastInputType  =  useRef ( '' ) ; 
198201  useEvent ( inputRef ,  'input' ,  e  =>  { 
@@ -346,7 +349,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
346349    return  ( )  =>  { 
347350      document . removeEventListener ( 'keyup' ,  onKeyUpCapture ,  true ) ; 
348351    } ; 
349-   } ,  [ onKeyUpCapture ] ) ; 
352+   } ,  [ ] ) ; 
350353
351354  let  stringFormatter  =  useLocalizedStringFormatter ( intlMessages ,  '@react-aria/autocomplete' ) ; 
352355  let  collectionProps  =  useLabels ( { 
0 commit comments