React 18.3测试中act警告问题的解决方式及版本变更咨询
手动包裹act不是正确解决方式
手动把userEvent.click再包一层act(...)只是临时屏蔽警告,并没有解决根本问题,甚至可能掩盖测试中未处理的异步更新逻辑,导致测试结果不可靠。
React 18.3的关键变更点
React 18.3对测试环境中的异步更新检测逻辑做了强化:
- 之前版本中,React可能会忽略一些在userEvent操作完成后延迟执行的微任务/宏任务更新(比如组件内部
Promise.resolve().then()、setTimeout里的状态变更) - 18.3版本会严格检测所有组件更新是否在
act边界内,只要有更新发生在act包裹的操作之外,就会抛出警告,目的是确保测试能捕获到所有状态变更,避免测试结果出现假阳性。
更合理的测试调整方案
针对这个警告,核心是确保测试等待所有异步操作完成,而不是手动添加冗余的act包裹:
1. 必须await userEvent操作
userEvent.click是异步函数,即使它内部已经被act包裹,测试中也需要用await等待其完成,确保所有同步和异步更新都被React捕获:
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import ToggleButton from '../ToggleButton'; test('切换按钮文本正确变更', async () => { render(<ToggleButton />); const button = screen.getByText('Light Mode'); // 等待点击操作及所有关联更新完成 await userEvent.click(button); expect(screen.getByText('Dark Mode')).toBeInTheDocument(); });
2. 处理组件内的延迟更新
如果你的ToggleButton组件内部存在异步逻辑(比如useEffect里的Promise、setTimeout),需要用waitFor或findBy*查询来等待这些更新完成:
test('处理异步更新的按钮文本切换', async () => { render(<ToggleButton />); const button = screen.getByText('Light Mode'); await userEvent.click(button); // 等待异步更新完成后再断言 await waitFor(() => { expect(screen.getByText('Dark Mode')).toBeInTheDocument(); }); // 或者用findByText(本身就是waitFor的封装) // await screen.findByText('Dark Mode'); });
3. 检查组件是否存在未处理的异步逻辑
如果上述调整后仍有警告,需要排查组件代码:
- 有没有在状态变更后触发延迟执行的逻辑(比如
setTimeout、requestAnimationFrame) - 有没有第三方库的异步回调导致状态更新
- 确保所有状态更新都在React的渲染周期内触发
内容的提问来源于stack exchange,提问作者Jacobdo
相关产品推荐
相关产品推荐

