为什么测试Material UI Select搜索框时toHaveFocus返回错误聚焦元素
问题根因
MUI Select 下拉菜单默认开启了自动聚焦第一个菜单项的内置逻辑,该逻辑的执行时机在测试环境(jsdom)中晚于你自定义的 useEffect 聚焦逻辑,导致输入框刚被聚焦就被 MUI 把焦点抢给了作为第一个菜单项的搜索容器。
浏览器实际运行时时序和测试环境存在差异,所以不会出现这个问题。
另外你的 useEffect 依赖数组写法存在问题:searchRef.current 不属于响应式状态,作为依赖放入数组不会触发 effect 重新执行,属于无效依赖。
解决方案
方案1:禁用MUI菜单默认自动聚焦行为(最推荐)
给 MUISelect 组件传入 MenuProps,关闭菜单自动聚焦首个菜单项的逻辑:
<MUISelect MenuProps={{ disableAutoFocusItem: true }} {/* 其余原有props */} > {/* 原有子元素 */} </MUISelect>
同时修正你的 useEffect 依赖数组:
useEffect(() => { if (searchRef.current && (isOpen || search)) { searchRef.current.focus(); } // 移除无效的searchRef.current依赖 }, [search, isOpen]);
方案2:延后输入框聚焦的执行时机
如果不想修改MUI默认行为,可以把聚焦逻辑放到下一个宏任务执行,确保覆盖MUI的默认聚焦结果:
useEffect(() => { if (searchRef.current && (isOpen || search)) { setTimeout(() => { searchRef.current.focus(); }, 0); } }, [search, isOpen]);
方案3:优化搜索框结构(可选优化)
不要把搜索框包在 MUIMenuItem 组件内部,避免它被MUI识别为菜单项参与聚焦逻辑,你可以直接用普通div作为搜索框容器:
<MUISelect> {/* 用普通div替代MUIMenuItem装搜索框 */} <div onClickCapture={stopImmediatePropagation} onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => { event.stopPropagation(); }} className={searchContainerClasses.root} > <Input inputRef={searchRef} endAdornment={<SearchIcon />} classes={searchClasses} placeholder={searchPlaceholder} onChange={onSearch} value={search} /> </div> {/* 其余菜单项 */} </MUISelect>
测试代码补充调整
如果选择方案2,可以保留原来的测试逻辑,也可以加短时间的waitFor确保聚焦完成:
it('should focus search input, when searchable and opened', async () => { const searchPlaceholder = 'search'; const { getByText, getByPlaceholderText } = render( <Filter {...props} searchable searchPlaceholder={searchPlaceholder} /> ); fireEvent.click(getByText(props.placeholder)); // 可选等待聚焦完成 await waitFor(() => { expect(getByPlaceholderText(searchPlaceholder)).toHaveFocus(); }) });
内容的提问来源于stack exchange,提问作者Itay Tur
相关产品推荐
相关产品推荐

