写Jest时必须setTimeout的情况

无法使用jest.useFakeTimers的情况: setTimeout中出现了ref.current,且使用这个ref的组件还未渲染

具体的业务场景是写了一个按钮,在点击之后Modal弹窗会显示,500ms后弹窗中的输入框会获得焦点

ts 复制代码
onClick={() => {
  open();
  setTimeout(() => {
    ref.current.focus();
  }, 500);
}}

如上代码所示,当你打算在setTimeout中访问ref.current时,且如果使用了如下代码

js 复制代码
// 指定 Jest 使用假的全局日期、性能、时间和定时器 API
beforeEach(() => {
  jest.useFakeTimers()
});
afterEach(() => {
  jest.runOnlyPendingTimers()
  jest.useRealTimers()
});

user.click(button)就会超时,此时,如果你使用waitFor将其包裹,则会看到真正的报错

js 复制代码
await waitFor(() => user.click(button));
javascript 复制代码
    TypeError: Cannot read properties of null (reading 'focus')

  36 |         setTimeout(() => {
> 37 |           ref.current.focus();
     |                       ^
  38 |         }, 500);
  39 |       }}

根因是因为jest.useFakeTimers运行了之后,setTimeout会立即执行,不会等待,而这时的ref.current还未被赋值。

React提供的act函数可以在包裹一段代码后,可以等待react组件渲染完成。

一般我们可以把它包在render函数之外,如下代码所示:

js 复制代码
it ('renders with button disabled', async () => {
  await act(async () => {
    root.render(<TestComponent />)
  });
  expect(container.querySelector('button')).toBeDisabled();
});

这样即使TestComponent中使用了useRef,对应的ref也能获取到值。

但由于我们测试的是modal组件,需要点击之后才显示,所以无法使用这个方案。

所以解决方案就是,删除jest.useFakeTimers相关代码,让setTimeout异步执行,这样ref才能在click后有时间获取到值。由于此时需要等待500ms,所以我们也需要setTimeout,由于此时react组件会更新,所以我们需要用act包裹,所以最终的解决方案就是

tsx 复制代码
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
it('点击按钮后,出现弹窗且输入框获得焦点', async () => {
  const user = userEvent.setup();
  render(
    <Modal>
      <Button>创建</Button>
    </Modal>
  );
  const button = screen.getByText('创建');
  await user.click(button);
  await act(async () => await sleep(500)); // 等待ref获得值并focus
  expect(screen.getByPlaceholderText('请输入')).toHaveFocus();
});
相关推荐
十步杀一人_千里不留行15 小时前
【实战教程】React Native项目集成Google ML Kit实现离线水表OCR识别
react native·react.js·ocr
桥豆麻袋939316 小时前
vite 初始化react项目
前端·react.js·前端框架
HelloRevit17 小时前
React -> AI组件 -> 调用Ollama模型, qwen3:1.7B非常聪明
前端·react.js·前端框架
1nv1s1ble18 小时前
React 笔记[1] hello world
前端·笔记·react.js
Jenna的海糖18 小时前
ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘
前端·vue.js·react.js·typescript
果冻kk2 天前
【实战教程】零基础搭建DeepSeek大模型聊天系统 - Spring Boot+React完整开发指南
spring boot·后端·react.js·deepseek
gaog2zh2 天前
0903Redux改造项目_用户信息_状态管理-react-仿低代码平台项目
react.js·redux
sunbyte2 天前
Three.js + React 实战系列 - 项目展示区开发详解 Projects 组件(3D 模型 + 动效 + 状态切换)✨
javascript·react.js·3d
天天进步20152 天前
React Hooks 深入浅出
javascript·react.js·ecmascript
哟哟耶耶2 天前
react-13react中外部css引入以及style内联样式(动态className与动态style)
前端·css·react.js