From 06025a930a3ae9e474ca8258b66ea19f743083fb Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 16 Oct 2026 11:19:11 -0500 Subject: [PATCH] perf(react-compiler): enable ButtonBase Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e632d4e3-54d2-4423-9888-8e37b00494a3 --- .changeset/react-compiler-button-base.md | 5 +++++ packages/react/script/react-compiler.mjs | 1 - packages/react/src/Button/ButtonBase.tsx | 8 +++----- packages/react/src/Button/__tests__/Button.test.tsx | 12 ++++++++++++ 4 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 .changeset/react-compiler-button-base.md diff --git a/.changeset/react-compiler-button-base.md b/.changeset/react-compiler-button-base.md new file mode 100644 index 00000000000..fadfa4b6063 --- /dev/null +++ b/.changeset/react-compiler-button-base.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Button, IconButton, LinkButton: Improve rendering performance with React Compiler support. diff --git a/packages/react/script/react-compiler.mjs b/packages/react/script/react-compiler.mjs index 43d9c7c6e45..4fc29fb8cf9 100644 --- a/packages/react/script/react-compiler.mjs +++ b/packages/react/script/react-compiler.mjs @@ -16,7 +16,6 @@ const unsupportedPatterns = [ 'src/ActionMenu/ActionMenu.tsx', 'src/AnchoredOverlay/AnchoredOverlay.tsx', 'src/Autocomplete/AutocompleteInput.tsx', - 'src/Button/ButtonBase.tsx', 'src/Dialog/Dialog.tsx', 'src/FilteredActionList/FilteredActionList.tsx', 'src/LabelGroup/LabelGroup.tsx', diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index d4747b94d4d..aaf1e8cfe03 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -7,6 +7,7 @@ import Spinner from '../Spinner' import CounterLabel from '../CounterLabel' import {useId} from '../hooks' import {ConditionalWrapper} from '../internal/components/ConditionalWrapper' +import {useDevOnlyEffect} from '../internal/hooks/useDevOnlyEffect' import {AriaStatus} from '../live-region' import {clsx} from 'clsx' import classes from './ButtonBase.module.css' @@ -75,15 +76,12 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f // Only include the loading aria-describedby if there is a loading state const ariaDescribedByIds = loading ? [loadingAnnouncementID, ariaDescribedBy] : [ariaDescribedBy] - if (__DEV__) { + useDevOnlyEffect(() => { if (notificationIndicator === 'leadingVisual' && !LeadingVisual) { // eslint-disable-next-line no-console console.warn('Button: `notificationIndicator="leadingVisual"` requires a `leadingVisual` prop.') } - // Validate that the element is a semantic button/anchor. - // This runs during render (not in an effect) to avoid a conditional hook call - // that prevents React Compiler from optimizing this component. const el = innerRef.current if ( el && @@ -94,7 +92,7 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f // eslint-disable-next-line no-console console.warn('This component should be an instanceof a semantic button or anchor') } - } + }) return ( { consoleSpy.mockRestore() }) + it('validates the rendered element after its ref is attached', () => { + const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + try { + render() + + expect(consoleSpy).toHaveBeenCalledWith('This component should be an instanceof a semantic button or anchor') + } finally { + consoleSpy.mockRestore() + } + }) + it('respects the "disabled" prop', () => { const onClick = vi.fn() const container = render(