Skip to content

Commit 3b58bed

Browse files
committed
use more local state
Instead of relying on re-renders to get access to certain DOM nodes, we can access them immediately if they are local.
1 parent 68e4355 commit 3b58bed

File tree

1 file changed

+20
-14
lines changed
  • packages/@headlessui-react/src/components/menu

1 file changed

+20
-14
lines changed

packages/@headlessui-react/src/components/menu/menu.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,11 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
184184
autoFocus = false,
185185
...theirProps
186186
} = props
187+
let internalButtonRef = useRef<HTMLButtonElement | null>(null)
187188
let getFloatingReferenceProps = useFloatingReferenceProps()
188189
let buttonRef = useSyncRefs(
189190
ref,
191+
internalButtonRef,
190192
useFloatingReference(),
191193
useEvent((element) => machine.send({ type: ActionTypes.SetButtonElement, element }))
192194
)
@@ -222,16 +224,17 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
222224
}
223225
})
224226

225-
let menuState = useSlice(machine, (state) => state.menuState)
226-
let buttonElement = useSlice(machine, (state) => state.buttonElement)
227-
let itemsElement = useSlice(machine, (state) => state.itemsElement)
227+
let [menuState, itemsElement] = useSlice(machine, (state) => [
228+
state.menuState,
229+
state.itemsElement,
230+
])
228231

229232
let handleClick = useEvent((event: ReactMouseEvent) => {
230233
if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
231234
if (disabled) return
232235
if (menuState === MenuState.Open) {
233236
flushSync(() => machine.send({ type: ActionTypes.CloseMenu }))
234-
buttonElement?.focus({ preventScroll: true })
237+
internalButtonRef.current?.focus({ preventScroll: true })
235238
} else {
236239
event.preventDefault()
237240
machine.send({
@@ -262,7 +265,7 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
262265
{
263266
ref: buttonRef,
264267
id,
265-
type: useResolveButtonType(props, buttonElement),
268+
type: useResolveButtonType(props, internalButtonRef.current),
266269
'aria-haspopup': 'menu',
267270
'aria-controls': itemsElement?.id,
268271
'aria-expanded': menuState === MenuState.Open,
@@ -345,19 +348,19 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
345348
setLocalItemsElement
346349
)
347350

348-
let buttonElement = useSlice(machine, (state) => state.buttonElement)
349-
let itemsElement = useSlice(machine, (state) => state.itemsElement)
351+
let [menuState, buttonElement] = useSlice(machine, (state) => [
352+
state.menuState,
353+
state.buttonElement,
354+
])
350355

351356
let portalOwnerDocument = useOwnerDocument(buttonElement)
352-
let ownerDocument = useOwnerDocument(itemsElement)
357+
let ownerDocument = useOwnerDocument(localItemsElement)
353358

354359
// Always enable `portal` functionality, when `anchor` is enabled
355360
if (anchor) {
356361
portal = true
357362
}
358363

359-
let menuState = useSlice(machine, (state) => state.menuState)
360-
361364
let usesOpenClosedState = useOpenClosed()
362365
let [visible, transitionData] = useTransition(
363366
transition,
@@ -380,7 +383,10 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
380383
// Mark other elements as inert when the menu is visible, and `modal` is enabled
381384
let inertOthersEnabled = __demoMode ? false : modal && menuState === MenuState.Open
382385
useInertOthers(inertOthersEnabled, {
383-
allowed: useCallback(() => [buttonElement, itemsElement], [buttonElement, itemsElement]),
386+
allowed: useCallback(
387+
() => [buttonElement, localItemsElement],
388+
[buttonElement, localItemsElement]
389+
),
384390
})
385391

386392
// We keep track whether the button moved or not, we only check this when the menu state becomes
@@ -400,16 +406,16 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
400406
let panelEnabled = didButtonMove ? false : visible
401407

402408
useEffect(() => {
403-
let container = itemsElement
409+
let container = localItemsElement
404410
if (!container) return
405411
if (menuState !== MenuState.Open) return
406412
if (container === ownerDocument?.activeElement) return
407413

408414
container.focus({ preventScroll: true })
409-
}, [menuState, itemsElement, ownerDocument])
415+
}, [menuState, localItemsElement, ownerDocument])
410416

411417
useTreeWalker(menuState === MenuState.Open, {
412-
container: itemsElement,
418+
container: localItemsElement,
413419
accept(node) {
414420
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT
415421
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP

0 commit comments

Comments
 (0)