act 是一个测试助手,用于在进行断言之前应用待处理的 React 更新。

await act(async actFn)

为了为断言准备一个组件,将渲染它和执行更新的代码封装在一个 await act() 调用中。这使你的测试运行方式更接近 React 在浏览器中的工作方式。

🌐 To prepare a component for assertions, wrap the code rendering it and performing updates inside an await act() call. This makes your test run closer to how React works in the browser.

注意

你可能会发现直接使用 act() 有点过于冗长。为了避免一些样板代码,你可以使用像 React Testing Library 这样的库,它的辅助工具被 act() 封装。


参考

🌐 Reference

await act(async actFn)

在编写 UI 测试时,像渲染、用户事件或数据获取这样的任务可以被视为与用户界面交互的“单元”。React 提供了一个名为 act() 的辅助工具,它可以确保在你进行任何断言之前,所有与这些“单元”相关的更新都已被处理并应用到 DOM。

🌐 When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.

act 这个名字来源于 Arrange-Act-Assert 模式。

🌐 The name act comes from the Arrange-Act-Assert pattern.

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

注意

我们建议将 actawaitasync 函数一起使用。虽然同步版本在许多情况下都能工作,但并非在所有情况下都有效,并且由于 React 内部调度更新的方式,很难预测何时可以使用同步版本。

🌐 We recommend using act with await and an async function. Although the sync version works in many cases, it doesn’t work in all cases and due to the way React schedules updates internally, it’s difficult to predict when you can use the sync version.

我们将在未来弃用并删除同步版本。

🌐 We will deprecate and remove the sync version in the future.

参数

🌐 Parameters

  • async actFn:一个异步函数,用于封装正在测试的组件的渲染或交互。在 actFn 中触发的任何更新都会被添加到内部 act 队列中,然后一起刷新以处理并应用对 DOM 的任何更改。由于它是异步的,React 还会运行穿越异步边界的任何代码,并刷新任何已计划的更新。

返回

🌐 Returns

act 不返回任何值。

用法

🌐 Usage

在测试一个组件时,你可以使用 act 对其输出进行断言。

🌐 When testing a component, you can use act to make assertions about its output.

例如,假设我们有这个 Counter 组件,下面的使用示例展示了如何测试它:

🌐 For example, let’s say we have this Counter component, the usage examples below show how to test it:

function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => prev + 1);
}

useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>
Click me
</button>
</div>
)
}

在测试中渲染组件

🌐 Rendering components in tests

要测试组件的渲染输出,请将渲染封装在 act() 内:

🌐 To test the render output of a component, wrap the render inside act():

import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';

it('can render and update a counter', async () => {
container = document.createElement('div');
document.body.appendChild(container);

// ✅ Render the component inside act().
await act(() => {
ReactDOMClient.createRoot(container).render(<Counter />);
});

const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
});

在这里,我们创建一个容器,将其附加到文档中,并在 act() 内渲染 Counter 组件。这确保组件在进行断言之前已被渲染并且其效果已被应用。

🌐 Here, we create a container, append it to the document, and render the Counter component inside act(). This ensures that the component is rendered and its effects are applied before making assertions.

使用 act 可以确保在我们进行断言之前已应用所有更新。

🌐 Using act ensures that all updates have been applied before we make assertions.

在测试中分派事件

🌐 Dispatching events in tests

要测试事件,将事件分发封装在 act() 内:

🌐 To test events, wrap the event dispatch inside act():

import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';

it.only('can render and update a counter', async () => {
const container = document.createElement('div');
document.body.appendChild(container);

await act( async () => {
ReactDOMClient.createRoot(container).render(<Counter />);
});

// ✅ Dispatch the event inside act().
await act(async () => {
button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});

const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});

在这里,我们使用 act 渲染组件,然后在另一个 act() 内部分发事件。这确保了在进行断言之前,事件的所有更新都已应用。

🌐 Here, we render the component with act, and then dispatch the event inside another act(). This ensures that all updates from the event are applied before making assertions.

易犯错误

不要忘记,只有当 DOM 容器被添加到文档中时,派发 DOM 事件才有效。你可以使用像 React Testing Library 这样的库来减少样板代码。

🌐 Don’t forget that dispatching DOM events only works when the DOM container is added to the document. You can use a library like React Testing Library to reduce the boilerplate code.

故障排除

🌐 Troubleshooting

我收到一个错误:“当前测试环境未配置为支持 act(…)”

🌐 I’m getting an error: “The current testing environment is not configured to support act(…)”

使用 act 需要在你的测试环境中设置 global.IS_REACT_ACT_ENVIRONMENT=true。这是为了确保 act 仅在正确的环境中使用。

🌐 Using act requires setting global.IS_REACT_ACT_ENVIRONMENT=true in your test environment. This is to ensure that act is only used in the correct environment.

如果你未设置全局变量,你将看到如下错误:

🌐 If you don’t set the global, you will see an error like this:

Console

要修复,请将其添加到 React 测试的全局设置文件中:

🌐 To fix, add this to your global setup file for React tests:

global.IS_REACT_ACT_ENVIRONMENT=true

注意

在像 React Testing Library 这样的测试框架中,IS_REACT_ACT_ENVIRONMENT 已经为你设置好了。

🌐 In testing frameworks like React Testing Library, IS_REACT_ACT_ENVIRONMENT is already set for you.