Skip to content

Commit

Permalink
Add act documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack Pope committed May 24, 2024
1 parent adb3bed commit 9f3ae08
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 3 deletions.
99 changes: 99 additions & 0 deletions src/content/reference/react/act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: act
---

<Intro>

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

</Intro>

<InlineToc />

---

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.

The name `act` comes from the [Arrange-Act-Assert](https://wiki.c2.com/?ArrangeActAssert) pattern.

```js
act(() => {
// render components
});
// make assertions
```

## Usage {/*usage*/}

<Note>
You might find using `act()` directly a bit too verbose. To avoid some of the boilerplate, you could use a library like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro), whose helpers are wrapped with `act()`.
</Note>

For example, let’s say we have this `Counter` component:

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

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

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

Here is how we can test it:

```js
import React from 'react';
import ReactDOM from 'react-dom/client';
import Counter from './Counter';

let container;

beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});

afterEach(() => {
document.body.removeChild(container);
container = null;
});

it('can render and update a counter', () => {
// Test first render and effect
React.act(() => {
ReactDOM.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');

// Test second render and effect
React.act(() => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});

```

<Pitfall>

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](https://testing-library.com/docs/react-testing-library/intro) to reduce the boilerplate code.

</Pitfall>
1 change: 1 addition & 0 deletions src/content/reference/react/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ In addition to [Hooks](/reference/react) and [Components](/reference/react/compo
* [`lazy`](/reference/react/lazy) lets you defer loading a component's code until it's rendered for the first time.
* [`memo`](/reference/react/memo) lets your component skip re-renders with same props. Used with [`useMemo`](/reference/react/useMemo) and [`useCallback`.](/reference/react/useCallback)
* [`startTransition`](/reference/react/startTransition) lets you mark a state update as non-urgent. Similar to [`useTransition`.](/reference/react/useTransition)
* [`act`](/reference/react/act) lets you wrap renders and interactions in tests to ensure updates have processed before making assertions.

---

Expand Down
10 changes: 7 additions & 3 deletions src/sidebarReference.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@
"title": "APIs",
"path": "/reference/react/apis",
"routes": [
{
"title": "act",
"path": "/reference/react/act"
},
{
"title": "cache",
"path": "/reference/react/cache",
Expand Down Expand Up @@ -342,8 +346,8 @@
"path": "/reference/rules",
"routes": [
{
"title": "Components and Hooks must be pure",
"path": "/reference/rules/components-and-hooks-must-be-pure"
"title": "Components and Hooks must be pure",
"path": "/reference/rules/components-and-hooks-must-be-pure"
},
{
"title": "React calls Components and Hooks",
Expand Down Expand Up @@ -429,4 +433,4 @@
]
}
]
}
}

0 comments on commit 9f3ae08

Please sign in to comment.