perf(core): hoist inline objects and callbacks in hot list components#3367
Open
orrgottlieb wants to merge 1 commit into
Open
perf(core): hoist inline objects and callbacks in hot list components#3367orrgottlieb wants to merge 1 commit into
orrgottlieb wants to merge 1 commit into
Conversation
Stops re-creating objects and closures in render for components that
are rendered per-item in lists (Combobox options, dropdown rows,
menu items, tabs). Each per-item allocation forces every memoized
child to re-reconcile on every parent update.
Specific changes:
- `BaseItem`: replace `{}` and `[]` empty-object defaults with frozen
module-scope constants (`EMPTY_ITEM_PROPS`, `EMPTY_TOOLTIP_PROPS`,
`EMPTY_ITEM`, `TEXT_TOOLTIP_PROPS`).
- `ComboboxOption`: wrap `style={{ height: optionLineHeight }}` in
`useMemo`.
- `ComboboxHelpers.dividerItemRenderer`: hoist the common-case
`style={{ height: DIVIDER_HEIGHT }}` to a constant.
- `DropdownBaseList`: `useMemo` the `style={{ maxHeight }}` object.
- `MenuItemIcon`: `useMemo` the conditional `{ backgroundColor }` style.
- `Tab`: replace `{} as TooltipProps` default with a frozen constant;
`useCallback` the inline `onClick`/`onKeyDown` handlers.
Audit finding #8.
Contributor
Code Review by Qodo
1. MenuItemIcon missing forwardRef
|
Contributor
|
📦 Bundle Size Analysis ✅ No bundle size changes detected. Unchanged Components
📊 Summary:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
Summary
Stops re-creating per-render objects and closures inside components that render per-item in lists. Each unstable reference forces memoized children to reconcile on every parent update; in a 100-row Combobox/Dropdown that allocation cost compounds on every keystroke.
Files changed
{}/[]empty-object defaults replaced with frozen module-scoped constants (EMPTY_ITEM_PROPS,EMPTY_TOOLTIP_PROPS,EMPTY_ITEM,TEXT_TOOLTIP_PROPS).useMemoaroundstyle={{ height: optionLineHeight }}.{ height: DIVIDER_HEIGHT }style.useMemoaroundstyle={{ maxHeight }}.useMemofor the conditional{ backgroundColor }style.EMPTY_TOOLTIP_PROPSconstant;useCallbackforonClick/onKeyDown.Why
From a performance audit (item #8): inline
style={{ }}and={() => ...}in list-item renderers cause N allocations per parent render where N is the list length. The fix is mechanical (hoist or memoize) and zero-API-change.Test plan
🤖 Generated with Claude Code
PR Type
Enhancement
Description
Hoist inline objects and callbacks in list-item components
useMemoto prevent per-render allocationsuseCallbackin Tab componentEliminates N allocations per parent render where N is list length
Improves performance in Combobox, Dropdown, Menu, and Tabs
Diagram Walkthrough
File Walkthrough
BaseItem.tsx
Hoist empty objects to frozen constantspackages/core/src/components/BaseItem/BaseItem.tsx
EMPTY_ITEM_PROPS,EMPTY_TOOLTIP_PROPS,EMPTY_ITEM,TEXT_TOOLTIP_PROPS{}with frozen constants in defaultparameters
TEXT_TOOLTIP_PROPSconstantComboboxHelpers.tsx
Hoist divider style to constantpackages/core/src/components/Combobox/ComboboxHelpers/ComboboxHelpers.tsx
DIVIDER_STYLEconstant for the common-case divider heightstyle
dividerItemRendererto use hoisted stylewhen height matches default
ComboboxOption.tsx
Memoize option height style objectpackages/core/src/components/Combobox/components/ComboboxOption/ComboboxOption.tsx
style={{ height: optionLineHeight }}inuseMemohookoptionLineHeightpropDropdownBaseList.tsx
Memoize dropdown list max-height stylepackages/core/src/components/Dropdown/components/DropdownBaseList/DropdownBaseList.tsx
useMemoimportstyle={{ maxHeight: maxMenuHeight }}inuseMemohookmaxMenuHeightpropMenuItemIcon.tsx
Memoize menu item icon background stylepackages/core/src/components/Menu/MenuItem/components/MenuItemIcon/MenuItemIcon.tsx
useMemoimportEMPTY_STYLEfrozen constant for empty style objectbackgroundColorstyle inuseMemohookTab.tsx
Memoize tab event handlers and tooltip propspackages/core/src/components/Tabs/Tab/Tab.tsx
useCallbackimportEMPTY_TOOLTIP_PROPSfrozen constant for default tooltip propshandleKeyDownfunction touseCallbackwithdependencies
onClickhandler touseCallbacknamedhandleClickhandleClickcallback reference