为何ReactTestUtil的act()在回调无返回值时不返回Promise?
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?
这个设计核心是明确区分同步与异步更新场景,背后有几个关键考量:
同步场景的清晰标识
Part A 专门对应纯同步的组件更新——比如组件渲染、状态更新全程没有异步逻辑(像setTimeout、网络请求、useEffect异步执行这类)。此时act()可以一次性完成所有同步渲染和状态处理,不需要异步等待。React 团队通过这种设计,让开发者一眼就能判断这段测试代码处理的是同步操作,没有异步逻辑需要等待。避免无意义的异步开销
如果不管回调有没有异步逻辑都返回 Promise,大量纯同步的测试代码会被迫套上await,既增加了不必要的异步运行开销,也会让代码意图变得模糊——其他开发者看到await act(...)时,会误以为这段代码里有异步逻辑,反而提升了理解成本。异步场景的主动声明
当组件存在异步更新(哪怕是你觉得“渲染耗时较长”的操作,若组件内部隐含异步逻辑),你需要通过返回值(具体值或 Promise)主动告诉act():“这里有需要等待的异步操作”。此时act()会把后续的异步更新也纳入包裹范围,确保所有状态更新完成后再结束,保证测试的一致性。
简单来说,React 团队是想通过签名区分,让开发者主动明确操作的同步/异步属性,避免模糊场景下的测试不稳定,同时减少不必要的异步处理开销。
内容的提问来源于stack exchange,提问作者lch

