File tree Expand file tree Collapse file tree
packages/components/src/navigable-container Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -7,6 +7,7 @@ import type { ForwardedRef } from 'react';
77 * WordPress dependencies
88 */
99import { forwardRef , useRef , useEffect , useCallback } from '@wordpress/element' ;
10+ import { useMergeRefs } from '@wordpress/compose' ;
1011import { focus } from '@wordpress/dom' ;
1112
1213/**
@@ -153,19 +154,10 @@ function UnforwardedNavigableContainer(
153154 getFocusableContext ,
154155 ] ) ;
155156
157+ const mergedRef = useMergeRefs ( [ containerRef , ref ] ) ;
158+
156159 return (
157- < div
158- ref = { ( node ) => {
159- containerRef . current = node ;
160-
161- if ( typeof ref === 'function' ) {
162- ref ( node ) ;
163- } else if ( ref ) {
164- ref . current = node ;
165- }
166- } }
167- { ...restProps }
168- >
160+ < div ref = { mergedRef } { ...restProps } >
169161 { children }
170162 </ div >
171163 ) ;
Original file line number Diff line number Diff line change @@ -215,6 +215,30 @@ describe( 'NavigableMenu', () => {
215215 expect ( externalWrapperOnKeyDownSpy ) . toHaveBeenCalledTimes ( 2 ) ;
216216 } ) ;
217217
218+ it ( 'should keep forwarded callback refs stable across rerenders' , ( ) => {
219+ const refSpy = jest . fn ( ) ;
220+
221+ const { rerender } = render (
222+ < NavigableMenu ref = { refSpy } >
223+ < button > Item 1</ button >
224+ </ NavigableMenu >
225+ ) ;
226+
227+ expect ( refSpy ) . toHaveBeenCalledTimes ( 1 ) ;
228+ expect ( refSpy ) . toHaveBeenCalledWith ( expect . any ( HTMLElement ) ) ;
229+
230+ rerender (
231+ < NavigableMenu ref = { refSpy } >
232+ < button > Item 1</ button >
233+ </ NavigableMenu >
234+ ) ;
235+
236+ // With a stable merged ref (useMergeRefs), the callback ref should
237+ // not be called again on rerender. Previously, an inline ref callback
238+ // would cause React to detach (null) and reattach on every render.
239+ expect ( refSpy ) . toHaveBeenCalledTimes ( 1 ) ;
240+ } ) ;
241+
218242 it ( 'skips its internal logic when the tab key is pressed' , async ( ) => {
219243 const user = userEvent . setup ( ) ;
220244
You can’t perform that action at this time.
0 commit comments