From 65e6c5290844c0f321b62dc13ed16b95ec720c01 Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Mon, 27 Jul 2026 22:51:58 +0200 Subject: [PATCH 01/12] refactor(Select): you might not need an effect --- .../Select/common/InternalSelect.tsx | 156 ++++++++---------- 1 file changed, 71 insertions(+), 85 deletions(-) diff --git a/src/components/Select/common/InternalSelect.tsx b/src/components/Select/common/InternalSelect.tsx index 4a9e823d8..6822e5900 100644 --- a/src/components/Select/common/InternalSelect.tsx +++ b/src/components/Select/common/InternalSelect.tsx @@ -8,7 +8,6 @@ import { forwardRef, isValidElement, useCallback, - useEffect, useId, useMemo, useRef, @@ -20,7 +19,6 @@ import { SelectGroupProps, SelectItemObject, SelectItemProps, - SelectOptionListItem, } from './types'; import { Error, FormElementContainer, FormRoot } from '@/components/FormContainer'; import { Portal } from '@radix-ui/react-popover'; @@ -230,8 +228,7 @@ export const InternalSelect = ({ const visibleList = useRef([]); const navigatable = useRef([]); const valueNode = useRef>(new Map()); - const [isInitialized, setInitialized] = useState(false); - const [list, setList] = useState([]); + const updateElements = useCallback( ({ disabled, value, title, heading, nodeProps }: CallbackProps) => { if (title.includes(search) || heading?.includes(search)) { @@ -244,6 +241,53 @@ export const InternalSelect = ({ }, [search] ); + + const list = useMemo(() => { + const lowerCasedSearch = search.toLowerCase(); + + if (options) { + return options.flatMap(option => { + if ('options' in option) { + const heading = getTextFromNodes(option.heading).toLowerCase(); + return (option.options ?? []).map(item => { + valueNode.current.set(item.value, item); + const title = getTextFromNodes(item.label).toLowerCase(); + if (title.includes(lowerCasedSearch) || heading?.includes(lowerCasedSearch)) { + visibleList.current.push(item.value); + if (!disabled) { + navigatable.current.push(item.value); + } + } + return { + heading, + disabled: item.disabled, + value: item.value, + title, + }; + }); + } else { + valueNode.current.set(option.value, option); + const title = getTextFromNodes(option.label).toLowerCase(); + if (title.includes(lowerCasedSearch)) { + visibleList.current.push(option.value); + if (!disabled) { + navigatable.current.push(option.value); + } + } + return { + disabled: option.disabled, + value: option.value, + title: getTextFromNodes(option.label), + }; + } + }); + } else if (children) { + return childrenToComboboxItemArray(children, updateElements); + } + + return []; + }, [children, disabled, options, search, updateElements]); + const onUpdateSearch = useCallback( (search: string) => { setSearch(search); @@ -274,62 +318,6 @@ export const InternalSelect = ({ [highlighted, list] ); - const updateList = useCallback( - (children?: ReactNode, options?: SelectOptionListItem[]) => { - const lowerCasedSearch = search.toLowerCase(); - if (options) { - setList( - options.flatMap(option => { - if ('options' in option) { - const heading = getTextFromNodes(option.heading).toLowerCase(); - return (option.options ?? []).map(item => { - valueNode.current.set(item.value, item); - const title = getTextFromNodes(item.label).toLowerCase(); - if ( - title.includes(lowerCasedSearch) || - heading?.includes(lowerCasedSearch) - ) { - visibleList.current.push(item.value); - if (!disabled) { - navigatable.current.push(item.value); - } - } - return { - heading, - disabled: item.disabled, - value: item.value, - title, - }; - }); - } else { - valueNode.current.set(option.value, option); - const title = getTextFromNodes(option.label).toLowerCase(); - if (title.includes(lowerCasedSearch)) { - visibleList.current.push(option.value); - if (!disabled) { - navigatable.current.push(option.value); - } - } - return { - disabled: option.disabled, - value: option.value, - title: getTextFromNodes(option.label), - }; - } - }) - ); - } else if (children) { - setList(childrenToComboboxItemArray(children, updateElements)); - } - }, - [disabled, search, updateElements] - ); - - useEffect(() => { - updateList(children, options); - setInitialized(true); - }, [children, options, updateList]); - const inputRef = useRef(null); const inputModalityProps = useInputModality(); const portalContainer = useResolvedPortalContainer(container); @@ -427,31 +415,29 @@ export const InternalSelect = ({ data-testid="select-trigger" {...triggerProps} > - {isInitialized && ( - - {selectedValues.length === 0 ? ( - placeholder - ) : multiple ? ( - - ) : ( - - )} - - )} + + {selectedValues.length === 0 ? ( + placeholder + ) : multiple ? ( + + ) : ( + + )} + Date: Mon, 27 Jul 2026 22:52:24 +0200 Subject: [PATCH 02/12] refactor(Select): better naming for internal vars --- src/components/Select/common/InternalSelect.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Select/common/InternalSelect.tsx b/src/components/Select/common/InternalSelect.tsx index 6822e5900..0251ea73a 100644 --- a/src/components/Select/common/InternalSelect.tsx +++ b/src/components/Select/common/InternalSelect.tsx @@ -242,7 +242,7 @@ export const InternalSelect = ({ [search] ); - const list = useMemo(() => { + const normalizedOptions = useMemo(() => { const lowerCasedSearch = search.toLowerCase(); if (options) { @@ -295,7 +295,7 @@ export const InternalSelect = ({ const visibleItemsList: string[] = []; const navigatableList: string[] = []; const searchLowerCase = search.toLowerCase(); - list.forEach(item => { + normalizedOptions.forEach(item => { if ( item.title.includes(searchLowerCase) || item.heading?.includes(searchLowerCase) @@ -315,7 +315,7 @@ export const InternalSelect = ({ setHighlighted(navigatableList[0] ?? null); } }, - [highlighted, list] + [highlighted, normalizedOptions] ); const inputRef = useRef(null); @@ -451,7 +451,7 @@ export const InternalSelect = ({ value={selectedValues} onChange={() => null} > - {list.map(item => ( + {normalizedOptions.map(item => (