Skip to content

perf(SelectPanel): memoize overlay props and callbacks - #7621

Merged
hectahertz merged 3 commits into
mainfrom
hectahertz/perf-select-panel-memo-context
Mar 12, 2026
Merged

perf(SelectPanel): memoize overlay props and callbacks#7621
hectahertz merged 3 commits into
mainfrom
hectahertz/perf-select-panel-memo-context

Conversation

@hectahertz

Copy link
Copy Markdown
Contributor

Closes #

SelectPanel was creating several new object/function references on every render that got passed as props to AnchoredOverlay:

  • overlayProps: complex nested object with style, role, aria attributes, created inline every render
  • focusTrapSettings: new object every render
  • preventBubbling: curried function creating a new closure every render
  • closeButtonProps: inline object literal every render

Memoized all of these. Also refactored preventBubbling from a curried function to a direct useCallback, passing the overlay's onKeyDown through the dependency array instead of the closure.

Changelog

New

N/A

Changed

  • Memoized overlayProps, focusTrapSettings, and preventBubbling to stabilize references across renders
  • Hoisted closeButtonProps to a module-level constant

Removed

N/A

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Pure memoization refactor, no behavioral changes. The preventBubbling function was refactored from curried to direct callback, but the logic is identical.

Merge checklist

  • Added/updated tests
  • Added/updated documentation
  • Added/updated previews (Storybook)
  • Changes are SSR compatible
  • Tested in Chrome
  • Tested in Firefox
  • Tested in Safari
  • Tested in Edge
  • (GitHub staff only) Integration tests pass at github/github-ui

@hectahertz
hectahertz requested a review from a team as a code ownerMarch 4, 2026 12:31
@changeset-bot

changeset-botBot commented Mar 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d66524d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@primer/reactPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actionsgithub-actionsBot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Mar 4, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Action required

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the integration-tests: skipped manually label to skip these checks.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors SelectPanel to reduce unnecessary allocations by memoizing several props/callbacks passed into AnchoredOverlay, helping stabilize references across renders.

Changes:

  • Memoizes focusTrapSettings and consolidated overlayProps into a mergedOverlayPropsuseMemo.
  • Refactors preventBubbling from a curried function to a direct useCallback.
  • Hoists closeButtonProps to a module-level constant and adds a patch changeset.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

FileDescription
packages/react/src/SelectPanel/SelectPanel.tsxMemoizes overlay-related props/callbacks to reduce re-renders/allocations when passing props to AnchoredOverlay.
.changeset/perf-select-panel-memo-context.mdAdds a patch changeset describing the perf-focused memoization refactor.
Comments suppressed due to low confidence (1)

packages/react/src/SelectPanel/SelectPanel.tsx:803

  • The double cast in overlayProps?.onKeyDown?.(event as unknown as React.KeyboardEvent<HTMLDivElement>) looks unnecessary because event is already a React.KeyboardEvent<HTMLDivElement>. If there’s a typing mismatch, it would be clearer to type/narrow overlayProps.onKeyDown (or the extracted overlayOnKeyDown) rather than casting the event to unknown.
 (event: React.KeyboardEvent<HTMLDivElement>) => {
overlayProps?.onKeyDown?.(event as unknown as React.KeyboardEvent<HTMLDivElement>)

Comment on lines 800 to +821
@@ -817,7 +816,35 @@ function Panel({

// if this is a typeahead event, don't propagate outside of menu
event.stopPropagation()
}
},
[overlayProps],
)

CopilotAIMar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preventBubbling closes over the entire overlayProps object and lists it in the dependency array. Since only overlayProps.onKeyDown is used, this will recreate the callback whenever any other overlay prop changes (defeating the memoization goal). Consider extracting const overlayOnKeyDown = overlayProps?.onKeyDown and depending on that instead (and call that inside the handler).

This issue also appears on line 801 of the same file.

Copilot uses AI. Check for mistakes.
} as React.CSSProperties,
onKeyDown: preventBubbling,
}),
[titleId, subtitle, subtitleId, overlayProps, variant, isKeyboardVisible, availablePanelHeight, preventBubbling],

CopilotAIMar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mergedOverlayProps only uses subtitle for a truthiness check to decide whether to set aria-describedby. Depending on the full subtitle value can cause recomputation when callers pass a new ReactElement each render even though the boolean presence hasn’t changed. Consider using Boolean(subtitle) (or subtitle != null) in the deps instead of subtitle to keep the memo effective.

Suggested change
[titleId,subtitle,subtitleId,overlayProps,variant,isKeyboardVisible,availablePanelHeight,preventBubbling],
[titleId,Boolean(subtitle),subtitleId,overlayProps,variant,isKeyboardVisible,availablePanelHeight,preventBubbling],

Copilot uses AI. Check for mistakes.

@jonrohanjonrohan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good, would be worth checking out integration tests also

@github-actions
github-actionsBottemporarily deployed to storybook-preview-7621 March 12, 2026 16:58 Inactive
@hectahertzhectahertz added the integration-tests: skipped manually Changes in this PR do not require an integration test label Mar 12, 2026
@hectahertz
hectahertz added this pull request to the merge queueMar 12, 2026
Merged via the queue into main with commit 235126aMar 12, 2026
57 checks passed
@hectahertz
hectahertz deleted the hectahertz/perf-select-panel-memo-context branch March 12, 2026 18:04
@primerprimerBot mentioned this pull request Mar 12, 2026
@primer-integration

Copy link
Copy Markdown

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/15932

@primer-integration

Copy link
Copy Markdown

Integration test results from github/github-ui:

Passed CI  Passed
Passed VRT  Passed
Passed Projects  Passed

All checks passed!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-tests: recommendedThis change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpmintegration-tests: skipped manuallyChanges in this PR do not require an integration test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@hectahertz@jonrohan