Skip to content

Commit aeb204a

Browse files
authored
perf(query-builder): Do not render filter value suggestions when they're not open (#103691)
This is an optimization for the `SearchQueryBuilder` component. Here's the issue I'm seeing: - `SearchQueryBuilder` is rendered (or re-rendered, whichever) with unstable props (because the props are hard to stabilize, which happens sometimes, or why rendering for the first time) - The render is fairly slow, it can take anywhere in the 50-100ms time range - One of the slower parts is rendering `SearchQueryBuilderComboBox`. This takes a long time to render because it uses React Stately's `useComboBoxState`. This hook accepts the `children` props, which is the collection _renderer_. It _renders all the items_ at this point, even if the combo box is not open! So, the entire collection is _rendered_ even if it's not open. This PR does a trick in which if we know the combo box is not open, it provides a `null` children, so the extra work doesn't have to be done. This saves ~30ms per free text token in the builder, which can be a _lot_, if there are many tokens, since there's free text around each one. As far as I can tell, this doesn't cause any adverse effects on the UI. **Before:** <img width="1031" height="363" alt="Screenshot 2025-11-19 at 4 50 55 PM" src="https://github.com/user-attachments/assets/b3954e66-8527-458c-960f-a9838f7b30bc" /> **After:** <img width="452" height="186" alt="Screenshot 2025-11-19 at 4 56 02 PM" src="https://github.com/user-attachments/assets/15436d5d-e384-4f9f-a4bc-06be670dad57" />
1 parent 5bc30b6 commit aeb204a

File tree

1 file changed

+26
-15
lines changed

1 file changed

+26
-15
lines changed

static/app/components/searchQueryBuilder/tokens/freeText.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import {FieldKind, FieldValueType} from 'sentry/utils/fields';
4141
import {isCtrlKeyPressed} from 'sentry/utils/isCtrlKeyPressed';
4242
import useOrganization from 'sentry/utils/useOrganization';
4343

44+
import type {FilterKeyItem} from './filterKeyListBox/types';
45+
4446
type SearchQueryBuilderInputProps = {
4547
item: Node<ParseResultToken>;
4648
state: ListState<ParseResultToken>;
@@ -409,6 +411,24 @@ function SearchQueryBuilderInputInternal({
409411
updateSelectionIndex();
410412
}, [updateSelectionIndex]);
411413

414+
const renderItem = useCallback(
415+
(keyItem: FilterKeyItem) =>
416+
itemIsSection(keyItem) ? (
417+
<Section title={keyItem.label} key={keyItem.key}>
418+
{keyItem.options.map(child => (
419+
<Item {...child} key={child.key}>
420+
{child.label}
421+
</Item>
422+
))}
423+
</Section>
424+
) : (
425+
<Item {...keyItem} key={keyItem.key}>
426+
{keyItem.label}
427+
</Item>
428+
),
429+
[]
430+
);
431+
412432
return (
413433
<Fragment>
414434
<HiddenText
@@ -690,21 +710,12 @@ function SearchQueryBuilderInputInternal({
690710
state.collection.getLastKey() === item.key ? 'query-builder-input' : undefined
691711
}
692712
>
693-
{keyItem =>
694-
itemIsSection(keyItem) ? (
695-
<Section title={keyItem.label} key={keyItem.key}>
696-
{keyItem.options.map(child => (
697-
<Item {...child} key={child.key}>
698-
{child.label}
699-
</Item>
700-
))}
701-
</Section>
702-
) : (
703-
<Item {...keyItem} key={keyItem.key}>
704-
{keyItem.label}
705-
</Item>
706-
)
707-
}
713+
{/* `useComboBoxState` inside the combo box component eagerly iterates
714+
`children`, which can be very slow if there are many items. If the combo
715+
box is not even open, do not pass any `children`. This prevents the
716+
combo box from iterating anything while it's closed, which improves
717+
render performance when the combo box is closed. */}
718+
{isOpen ? renderItem : null}
708719
</SearchQueryBuilderCombobox>
709720
</Fragment>
710721
);

0 commit comments

Comments
 (0)