act() đảm bảo React flush mọi state update + effect trước khi assertion chạy — mô phỏng đúng behavior browser. Không có act → console warning "An update was not wrapped in act(...)" và assertion có thể đọc state cũ.
React Testing Library tự wrap act() cho bạn trong: render(), userEvent, fireEvent. Đa số case không cần gọi act() thủ công.
import { render, screen, fireEvent } from '@testing-library/react'
// ❌ Double-wrap không cần — fireEvent đã wrap act
act(() => { fireEvent.click(button) })
// ✅ Standard
fireEvent.click(button)
expect(screen.getByText('Updated')).toBeInTheDocument()
// ✅ Async update — KHÔNG dùng manual act(async () => {})
// Thay bằng findBy* hoặc waitFor (đã handle act + retry)
const item = await screen.findByText('Loaded')
await waitFor(() => {
expect(api.fetch).toHaveBeenCalled()
})Quy tắc: nếu thấy warning "not wrapped in act", thường là do quên await cho async query, không phải thiếu act() thật.
act() ensures React flushes all state updates + effects before assertions run — mimicking real browser behavior. Without it → "An update was not wrapped in act(...)" warning, and assertions may read stale state.
React Testing Library wraps act() for you inside: render(), userEvent, fireEvent. In most cases you do not need to call act() manually.
import { render, screen, fireEvent } from '@testing-library/react'
// ❌ Unnecessary double-wrap — fireEvent already wraps act
act(() => { fireEvent.click(button) })
// ✅ Standard
fireEvent.click(button)
expect(screen.getByText('Updated')).toBeInTheDocument()
// ✅ Async updates — do NOT use manual act(async () => {})
// Use findBy* or waitFor (they handle act + retry)
const item = await screen.findByText('Loaded')
await waitFor(() => {
expect(api.fetch).toHaveBeenCalled()
})Rule: if you see "not wrapped in act" warnings, the cause is usually a missing await on an async query — not a missing act() call.