From 1aac15f383ceb4bb24738206ed8af258d2c96840 Mon Sep 17 00:00:00 2001 From: Joao Silva <100691367+JoaoSilvaGrafana@users.noreply.github.com> Date: Wed, 21 Jun 2023 18:28:46 +0900 Subject: [PATCH] Select: Performance improvements when opening menu and when hovering over options (#69230) --- .../grafana-ui/src/components/Select/SelectBase.tsx | 12 +++++++++--- .../grafana-ui/src/components/Select/SelectMenu.tsx | 6 +++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/grafana-ui/src/components/Select/SelectBase.tsx b/packages/grafana-ui/src/components/Select/SelectBase.tsx index ac82de6afe6..8a2fd262eb2 100644 --- a/packages/grafana-ui/src/components/Select/SelectBase.tsx +++ b/packages/grafana-ui/src/components/Select/SelectBase.tsx @@ -21,7 +21,7 @@ import { SingleValue } from './SingleValue'; import { ValueContainer } from './ValueContainer'; import { getSelectStyles } from './getSelectStyles'; import { useCustomSelectStyles } from './resetSelectStyles'; -import { ActionMeta, SelectBaseProps } from './types'; +import { ActionMeta, InputActionMeta, SelectBaseProps } from './types'; import { cleanValue, findSelectedValue, omitDescriptions } from './utils'; interface ExtraValuesIndicatorProps { @@ -155,6 +155,7 @@ export function SelectBase({ const reactSelectRef = useRef<{ controlRef: HTMLElement }>(null); const [closeToBottom, setCloseToBottom] = useState(false); const selectStyles = useCustomSelectStyles(theme, width); + const [hasInputValue, setHasInputValue] = useState(!!inputValue); // Infer the menu position for asynchronously loaded options. menuPlacement="auto" doesn't work when the menu is // automatically opened when the component is created (it happens in SegmentSelect by setting menuIsOpen={true}). @@ -224,7 +225,9 @@ export function SelectBase({ defaultValue, // Also passing disabled, as this is the new Select API, and I want to use this prop instead of react-select's one disabled, - filterOption, + // react-select always tries to filter the options even at first menu open, which is a problem for performance + // in large lists. So we set it to not try to filter the options if there is no input value. + filterOption: hasInputValue ? filterOption : null, getOptionLabel, getOptionValue, hideSelectedOptions, @@ -250,7 +253,10 @@ export function SelectBase({ menuShouldScrollIntoView: false, onBlur, onChange: onChangeWithEmpty, - onInputChange, + onInputChange: (val: string, actionMeta: InputActionMeta) => { + setHasInputValue(!!val); + onInputChange?.(val, actionMeta); + }, onKeyDown, onMenuClose: onCloseMenu, onMenuOpen: onOpenMenu, diff --git a/packages/grafana-ui/src/components/Select/SelectMenu.tsx b/packages/grafana-ui/src/components/Select/SelectMenu.tsx index 378a2640aa7..4224a0b147e 100644 --- a/packages/grafana-ui/src/components/Select/SelectMenu.tsx +++ b/packages/grafana-ui/src/components/Select/SelectMenu.tsx @@ -99,6 +99,10 @@ export const SelectMenuOptions = ({ const theme = useTheme2(); const styles = getSelectStyles(theme); const icon = data.icon ? toIconName(data.icon) : undefined; + // We are removing onMouseMove and onMouseOver from innerProps because they cause the whole + // list to re-render everytime the user hovers over an option. This is a performance issue. + // See https://github.com/JedWatson/react-select/issues/3128#issuecomment-451936743 + const { onMouseMove, onMouseOver, ...rest } = innerProps; return (