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 c33997690f5..b86a5f1fc72 100644 --- a/packages/react/src/ActionMenu/ActionMenu.tsx +++ b/packages/react/src/ActionMenu/ActionMenu.tsx @@ -117,7 +117,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 @@ -128,8 +130,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, @@ -145,7 +146,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. @@ -158,15 +159,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 @@ -178,14 +182,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}