Chakra UI Menu显示时搜索输入框频繁失焦问题排查
搜索输入框连续输入时失去焦点问题排查
我实现了一个带搜索结果展示的输入框,搜索过程中Input组件内会显示加载指示器,但连续输入多个字符、搜索结果更新时,输入框会失去焦点。使用的Chakra UI版本为@chakra-ui/react ^3.4.0。
相关代码
export const HomeSearch = () => { const { search, results, setResults } = useSearch(); const [query, setQuery] = useState(''); const router = useRouter(); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const searchFlies = debounce(async (searchQuery: string) => { if (!searchQuery.trim()) { setResults(undefined); return; } setIsLoading(true); try { await search(searchQuery); } catch (error) { console.error('Search error:', error); setResults(undefined); } finally { setIsLoading(false); } }, 300); searchFlies(query); return () => { searchFlies.cancel(); }; }, [query, search, setResults]); const handleClick = ({ id, type }: { id: string, type: string }) => { router.push(`${type}/${id}`); }; const cleaned = sortedArrayAZ<SearchResultShape>(results || []); return ( <Flex justify="center" marginTop={-8} zIndex={1}> <Box> <InputGroup endElement={ isLoading ? ( <Spinner color={'brand.primary'} /> ) : ( <IoIosCloseCircleOutline cursor={'pointer'} onClick={() => setQuery('')} size={32} /> ) } flex="1" startElement={<IoIosSearch size={32} />} zIndex={1000} > <Input autoFocus borderRadius={32} boxShadow="lg" id="homeSearch" onChange={(e) => setQuery(e.target.value)} p={8} placeholder={'Search by fly name or species'} ps={16} value={query} w={{ base: '90vw', md: '50vw' }} /> </InputGroup> <Menu.Root open={!!cleaned?.length || (!!query.length && !isLoading)}> <Menu.Content marginTop={-8} maxH={'35vh'} overflow={'scroll'} paddingTop={12} position={'absolute'} w={{ base: '90vw', md: '50vw' }} zIndex={500} > <Skeleton loading={false}> {cleaned?.length ? ( cleaned.map((result) => ( <Menu.Item justifyContent={'space-between'} key={result.id} my={2} onClick={() => handleClick({ id: result.id, type: result.type }) } value={result.id} > <HStack> <Avatar colorPalette={'blue'} name={result.name} src={result.thumbnail} /> <Text>{removeAZ(result.name)}</Text> </HStack> <HStack> {result.metadata.map((meta) => ( <MetaBadge key={meta} text={meta} /> ))} </HStack> </Menu.Item> )) ) : ( <Menu.Item justifyContent={'space-between'} my={2} value={'uknown'} > <Text>Unable to find results for {query}</Text> </Menu.Item> )} </Skeleton> </Menu.Content> </Menu.Root> </Box> </Flex> ); };
已尝试的无效方案
- 将debounce移入useCallback,但ESLint报错:
React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead. eslintreact-hooks/exhaustive-deps - 添加ref并通过useEffect监听结果,调用
focus()聚焦输入框
问题原因
1. InputGroup子元素切换导致DOM重建
当isLoading状态变化时,InputGroup的endElement会在<Spinner>和<IoIosCloseCircleOutline>之间完全替换。Chakra UI的InputGroup在子元素替换时,会触发整个输入容器的重渲染,输入框DOM节点被重建,自然失去焦点。
2. Menu.Root的open状态频繁变动引发干扰
Menu.Root的open状态依赖cleaned.length、query.length和isLoading三个变量:
- 输入时
query变化触发debounce,isLoading从false变为true,此时open状态会切换(因为!isLoading变为false) - 搜索完成后
isLoading变回false,同时results更新导致cleaned.length变化,open状态再次切换
这种频繁的菜单显示/隐藏会引发DOM重排,干扰输入框的焦点状态。
3. debounce函数重复创建的间接影响
每次query更新时,useEffect都会重新创建一个新的searchFlies debounce函数。虽然调用了cancel,但状态更新的时序可能导致不必要的组件重渲染,进一步加剧焦点丢失的问题。
内容的提问来源于stack exchange,提问作者Phil Lucks
相关产品推荐
相关产品推荐

