From a9d885c8446cc9422ac9c27edf0090df7313dce1 Mon Sep 17 00:00:00 2001 From: hectahertz Date: Wed, 4 Mar 2026 11:59:37 +0100 Subject: [PATCH 1/2] perf(ActionMenu): memoize context values to prevent unnecessary re-renders --- packages/react/src/ActionMenu/ActionMenu.tsx | 56 +++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx index e501b117377..cbaf0015479 100644 --- a/packages/react/src/ActionMenu/ActionMenu.tsx +++ b/packages/react/src/ActionMenu/ActionMenu.tsx @@ -164,22 +164,22 @@ const Menu: FCWithSlotMarker> = ({ } }) - return ( - - {contents} - + const isSubmenu = parentMenuContext.isSubmenu !== undefined + + const menuContextValue = useMemo( + () => ({ + anchorRef, + renderAnchor, + anchorId, + open: combinedOpenState, + onOpen, + onClose, + isSubmenu, + }), + [anchorRef, renderAnchor, anchorId, combinedOpenState, onOpen, onClose, isSubmenu], ) + + return {contents} } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -320,6 +320,20 @@ const Overlay: FCWithSlotMarker> = ({ } }, [anchorRef]) + const afterSelect = useCallback(() => onClose?.('item-select'), [onClose]) + + const overlayContextValue = useMemo( + () => ({ + container: 'ActionMenu' as const, + listRole: 'menu' as const, + listLabelledBy: ariaLabelledby || anchorAriaLabelledby || anchorId, + selectionAttribute: 'aria-checked' as const, + afterSelect, + enableFocusZone: isNarrowFullscreen, + }), + [ariaLabelledby, anchorAriaLabelledby, anchorId, afterSelect, isNarrowFullscreen], + ) + const featureFlagDisplayInViewportInsideDialog = useFeatureFlag( 'primer_react_action_menu_display_in_viewport_inside_dialog', ) @@ -351,17 +365,7 @@ const Overlay: FCWithSlotMarker> = ({ {...(overlayProps.overflow ? {[`data-overflow-${overlayProps.overflow}`]: ''} : {})} {...(overlayProps.maxHeight ? {[`data-max-height-${overlayProps.maxHeight}`]: ''} : {})} > - onClose?.('item-select'), - enableFocusZone: isNarrowFullscreen, // AnchoredOverlay takes care of focus zone. We only want to enable this if menu is narrow fullscreen. - }} - > + {children} From af32e81c45b6460b53e0ae9011db08f2b85b2a24 Mon Sep 17 00:00:00 2001 From: hectahertz Date: Wed, 4 Mar 2026 11:59:45 +0100 Subject: [PATCH 2/2] chore: changeset --- .changeset/perf-action-menu-memo-context.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/perf-action-menu-memo-context.md diff --git a/.changeset/perf-action-menu-memo-context.md b/.changeset/perf-action-menu-memo-context.md new file mode 100644 index 00000000000..1e49bf75e99 --- /dev/null +++ b/.changeset/perf-action-menu-memo-context.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Memoize ActionMenu context values to prevent unnecessary re-renders of menu items