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

为何ReactTestUtil的act()在回调无返回值时不返回Promise?

ReactTestUtils act() 设计疑问

ReactTestUtils 的 act() 方法有两个签名:

export function act(callback: () => VoidOrUndefinedOnly): void;        // Part A
export function act<T>(callback: () => T | Promise<T>): Promise<T>;    // Part B

我理解 Part B 的设计,但在使用 Part A 时遇到了困惑:当渲染一个DOM渲染耗时较长的组件 ComplicatedComponent 时,执行以下代码:

act(() => {
    ReactDOM.render(<ComplicatedComponent />, document.createElement("div"));
});    

此时 act() 不会返回 Promise<void>,导致无法使用 async/await 语法——如果强行使用会收到类型警告:

// warning: 'await' has no effect on the type of this expression

await act(() => {
    ReactDOM.render(<ComplicatedComponent />, document.createElement("div"));
});

只有让回调返回一个值(哪怕是 null),才能触发 Part B 的签名,让 act() 返回 Promise 从而正常使用 await:

// OK now

await act(() => {
    ReactDOM.render(<ComplicatedComponent />, document.createElement("div"));
    return null;
});

请问为何 ReactTestUtils 的 act() 会被设计为:当回调函数无返回值时不返回 Promise?


解答

这个设计核心是明确区分同步与异步更新场景,背后有几个关键考量:

  1. 同步场景的清晰标识
    Part A 专门对应纯同步的组件更新——比如组件渲染、状态更新全程没有异步逻辑(像 setTimeout、网络请求、useEffect 异步执行这类)。此时 act() 可以一次性完成所有同步渲染和状态处理,不需要异步等待。React 团队通过这种设计,让开发者一眼就能判断这段测试代码处理的是同步操作,没有异步逻辑需要等待。

  2. 避免无意义的异步开销
    如果不管回调有没有异步逻辑都返回 Promise,大量纯同步的测试代码会被迫套上 await,既增加了不必要的异步运行开销,也会让代码意图变得模糊——其他开发者看到 await act(...) 时,会误以为这段代码里有异步逻辑,反而提升了理解成本。

  3. 异步场景的主动声明
    当组件存在异步更新(哪怕是你觉得“渲染耗时较长”的操作,若组件内部隐含异步逻辑),你需要通过返回值(具体值或 Promise)主动告诉 act():“这里有需要等待的异步操作”。此时 act() 会把后续的异步更新也纳入包裹范围,确保所有状态更新完成后再结束,保证测试的一致性。

简单来说,React 团队是想通过签名区分,让开发者主动明确操作的同步/异步属性,避免模糊场景下的测试不稳定,同时减少不必要的异步处理开销。


内容的提问来源于stack exchange,提问作者lch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:46:22