You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

useEffect中setTimeout仅触发一次?Jest单元测试问题求助

问题原因分析及解决方案

1. 原代码的基础笔误

首先你代码里的状态声明写错了,应该是useState而非useEffect:

const [countDown, setCountDown] = useState(60); // 修正useState

2. 核心问题:闭包捕获旧值 + 测试中定时器的执行逻辑

闭包导致的state更新异常

你的setTimeout回调里用了setCountDown(countDown - 1),这里的countDown是当前effect执行时捕获的旧值:

  • 初始countDown=60时,useEffect设置的定时器回调里,countDown固定为60,触发后将state设为59。
  • 当countDown更新为59后,useEffect重新执行,会设置新的定时器,但此时测试中一次性快进2100ms的操作,并没有正确处理这个新生成的定时器——因为第一次定时器触发后,组件更新、新定时器创建的过程,没有在act中被完整处理,导致新定时器的回调没有被执行。

测试中定时器的执行问题

jest.advanceTimersByTime(2100)会一次性快进时间,但第一次定时器在1000ms时触发,执行setState后,组件会重新渲染并创建新的定时器(到期时间为2000ms)。但jest的fake timers在单次advance操作中,不会自动处理回调中新增的定时器的执行,需要确保每次状态更新后的副作用都被act包裹处理。

3. 修复方案

代码层面修复

  • 使用函数式更新避免闭包问题:
  • 添加定时器清理逻辑,避免内存泄漏:
const [countDown, setCountDown] = useState(60);
useEffect(() => {
  let countdownTimeout;
  if (countDown !== 0) {
    countdownTimeout = setTimeout(() => {
      // 用函数式更新,基于最新state计算
      setCountDown(prevCount => prevCount - 1);
    }, 1000);
  }
  // 清理定时器
  return () => clearTimeout(countdownTimeout);
}, [countDown]);

测试代码修复

分两次执行定时器快进,确保每次状态更新都被act处理:

it('countdown', async () => {
  jest.useFakeTimers();
  const { queryByText } = renderOnboardingVerificationScreen();

  expect(queryByText('60')).toBeTruthy();

  // 第一次快进1000ms,countDown变为59
  await act(async () => jest.advanceTimersByTime(1000));
  expect(queryByText('59')).toBeTruthy();

  // 第二次快进1000ms,countDown变为58
  await act(async () => jest.advanceTimersByTime(1000));
  expect(queryByText('58')).toBeTruthy();

  jest.useRealTimers();
});

或者使用runAllTimers结合act,直接让所有定时器执行完毕(适合测试到0的场景):

it('countdown to 0', async () => {
  jest.useFakeTimers();
  const { queryByText } = renderOnboardingVerificationScreen();

  await act(async () => jest.runAllTimers());
  expect(queryByText('0')).toBeTruthy();

  jest.useRealTimers();
});

内容的提问来源于stack exchange,提问作者Hoàng Đồng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:42:47