From c872610c53ab76aadf7b5a0a529cfd91f706c436 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 16 Oct 2026 11:21:00 -0500 Subject: [PATCH] perf(react-compiler): enable ActionMenu Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e632d4e3-54d2-4423-9888-8e37b00494a3 --- .changeset/react-compiler-action-menu.md | 5 +++++ packages/react/script/react-compiler.mjs | 1 - packages/react/src/ActionMenu/ActionMenu.tsx | 22 ++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 .changeset/react-compiler-action-menu.md diff --git a/.changeset/react-compiler-action-menu.md b/.changeset/react-compiler-action-menu.md new file mode 100644 index 00000000000..26e5af3c7f7 --- /dev/null +++ b/.changeset/react-compiler-action-menu.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +ActionMenu: Improve rendering performance with React Compiler support. diff --git a/packages/react/script/react-compiler.mjs b/packages/react/script/react-compiler.mjs index 4fc29fb8cf9..eda340e08dd 100644 --- a/packages/react/script/react-compiler.mjs +++ b/packages/react/script/react-compiler.mjs @@ -13,7 +13,6 @@ const files = glob }) const unsupportedPatterns = [ 'src/ActionMenu/ActionMenu.test.tsx', - 'src/ActionMenu/ActionMenu.tsx', 'src/AnchoredOverlay/AnchoredOverlay.tsx', 'src/Autocomplete/AutocompleteInput.tsx', 'src/Dialog/Dialog.tsx', diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx index 63c5c1f848a..5ffaecbcaf1 100644 --- a/packages/react/src/ActionMenu/ActionMenu.tsx +++ b/packages/react/src/ActionMenu/ActionMenu.tsx @@ -116,7 +116,9 @@ const Menu: FCWithSlotMarker> = ({ const anchorRef = useProvidedRefOrCreate(externalAnchorRef) const anchorId = useId(menuButtonChildId) - let renderAnchor: AnchoredOverlayProps['renderAnchor'] = null + const anchor: Pick = { + renderAnchor: null, + } // 🚨 Hack for good API! // we strip out Anchor from children and pass it to AnchoredOverlay to render // with additional props for accessibility @@ -127,8 +129,7 @@ const Menu: FCWithSlotMarker> = ({ // tooltip trigger const anchorChildren = child.props.children if (anchorChildren.type === MenuButton || isSlot(anchorChildren, MenuButton)) { - // eslint-disable-next-line react-hooks/immutability - renderAnchor = anchorProps => { + anchor.renderAnchor = anchorProps => { // We need to attach the anchor props to the tooltip trigger (ActionMenu.Button's grandchild) not the tooltip itself. const triggerButton = React.cloneElement( anchorChildren, @@ -144,7 +145,7 @@ const Menu: FCWithSlotMarker> = ({ anchorChildren !== undefined ? anchorChildren.type === Tooltip || isSlot(anchorChildren, Tooltip) : false if (isWrappedWithTooltip) { if (anchorChildren.props.children !== null) { - renderAnchor = anchorProps => { + anchor.renderAnchor = anchorProps => { // ActionMenu.Anchor's children can be wrapped with Tooltip. If this is the case, our anchor is the tooltip's trigger const tooltipTrigger = anchorChildren.props.children // We need to attach the anchor props to the tooltip trigger not the tooltip itself. @@ -157,15 +158,18 @@ const Menu: FCWithSlotMarker> = ({ } } } else { - renderAnchor = anchorProps => - React.cloneElement(child, { + anchor.renderAnchor = anchorProps => { + return React.cloneElement(child, { ...anchorProps, className: clsx(anchorProps.className, child.props.className), }) + } } return null } else if (child.type === MenuButton || isSlot(child, MenuButton)) { - renderAnchor = anchorProps => React.cloneElement(child, mergeAnchorHandlers(anchorProps, child.props)) + anchor.renderAnchor = anchorProps => { + return React.cloneElement(child, mergeAnchorHandlers(anchorProps, child.props)) + } return null } else { return child @@ -177,14 +181,14 @@ const Menu: FCWithSlotMarker> = ({ const menuContextValue = useMemo( () => ({ anchorRef, - renderAnchor, + renderAnchor: anchor.renderAnchor, anchorId, open: combinedOpenState, onOpen, onClose, isSubmenu, }), - [anchorRef, renderAnchor, anchorId, combinedOpenState, onOpen, onClose, isSubmenu], + [anchorRef, anchor.renderAnchor, anchorId, combinedOpenState, onOpen, onClose, isSubmenu], ) return {contents}