How to fix This error can occur when testing async state updates without wrapping in act() in React

ReactINTERMEDIATELOWUpdated Dec 11, 2025· 114 views

This warning appears in React Testing Library when asynchronous state updates occur outside of act() calls in tests. It indicates that React state changes are happening without proper test synchronization, which can lead to flaky tests and false positives. The fix involves wrapping async operations with act() or using Testing Library's async utilities.

Quick fix

Fixes This error can occur when testing async state updates without wrapping in act()

// Example 1: Async function in useEffect
useEffect(() => {
  const fetchData = async () => {
    const data = await api.getData();
    setData(data); // State update happens async
  };
  fetchData();
}, []);

// Example 2: Event handler with async operation
const handleClick = async () => {
  const result = await submitForm();
  setResult(result); // State update after async call
};

// Example 3: Timer-based updates
useEffect(() => {
  const timer = setTimeout(() => {
    setLoaded(true); // State update after timeout
  }, 1000);
  return () => clearTimeout(timer);
}, []);
  1. Identify the async state update
  2. Wrap async operations with act()
  3. Use Testing Library's async utilities
  4. Handle promise chains properly
react — error
// Example 1: Async function in useEffect
useEffect(() => {
const fetchData = async () => {
const data = await api.getData();
setData(data); // State update happens async
};
fetchData();
}, []);
// Example 2: Event handler with async operation
const handleClick = async () => {
const result = await submitForm();
setResult(result); // State update after async call
};
// Example 3: Timer-based updates
useEffect(() => {
const timer = setTimeout(() => {
setLoaded(true); // State update after timeout
}, 1000);
return () => clearTimeout(timer);
}, []);
This error can occur when testing async state updates without wrapping in act()

What this error means

The `act()` function is React's testing utility that ensures all state updates and side effects are processed before assertions are made. When you see the warning "This error can occur when testing async state updates without wrapping in act()", it means React detected state changes happening outside of an `act()` call during asynchronous operations. This typically occurs when: 1. **Async state updates** happen after a test has "finished" (like promises resolving after `await`) 2. **Timers** (setTimeout, setInterval) trigger state changes 3. **Event handlers** cause state updates asynchronously 4. **API calls** or data fetching completes after test assertions React throws this warning to prevent "flaky tests" - tests that pass or fail randomly depending on timing. Without `act()`, state updates might not be fully processed before assertions run, leading to false negatives (tests failing when they shouldn't) or false positives (tests passing when they shouldn't).

Typical symptoms

  • Warning appears in test console: 'This error can occur when testing async state updates without wrapping in act()'
  • Tests pass but show warnings in the console output
  • Flaky test behavior - tests sometimes pass, sometimes fail
  • State-dependent assertions fail even though components appear to work correctly
  • The warning appears when testing components with useEffect, async functions, or timers
  • Tests involving user interactions (clicks, typing) that trigger async updates

Common causes

  1. Using async/await without wrapping in act() when state updates occur
  2. Testing components with setTimeout/setInterval that update state
  3. Event handlers that trigger asynchronous state changes
  4. API calls or data fetching that completes after test assertions
  5. Using fireEvent without await for async event handlers
  6. Missing await on userEvent interactions that trigger async updates
  7. State updates happening in promise .then() or .catch() callbacks
  8. Using waitFor without proper async handling

How to fix "This error can occur when testing async state updates without wrapping in act()"

1Identify the async state update

First, locate where the async state update is happening. Common patterns include:

javascript
// Example 1: Async function in useEffect
useEffect(() => {
  const fetchData = async () => {
    const data = await api.getData();
    setData(data); // State update happens async
  };
  fetchData();
}, []);

// Example 2: Event handler with async operation
const handleClick = async () => {
  const result = await submitForm();
  setResult(result); // State update after async call
};

// Example 3: Timer-based updates
useEffect(() => {
  const timer = setTimeout(() => {
    setLoaded(true); // State update after timeout
  }, 1000);
  return () => clearTimeout(timer);
}, []);

Look for setState calls inside async functions, .then() callbacks, or timer callbacks.

2Wrap async operations with act()

Use React's act() function from @testing-library/react to wrap asynchronous operations:

javascript
import { render, screen, act } from '@testing-library/react';

test('async state update', async () => {
  render(<MyComponent />);

  // Wrap the async operation in act()
  await act(async () => {
    // Trigger the async state update
    await userEvent.click(screen.getByText('Load Data'));
  });

  // Now assertions will wait for state updates
  expect(screen.getByText('Data loaded')).toBeInTheDocument();
});

For timer-based updates:

javascript
import { act } from '@testing-library/react';

test('timer update', () => {
  jest.useFakeTimers();
  render(<ComponentWithTimer />);

  act(() => {
    jest.advanceTimersByTime(1000); // Advance timer
  });

  expect(screen.getByText('Timer done')).toBeInTheDocument();
  jest.useRealTimers();
});
3Use Testing Library's async utilities

React Testing Library provides utilities that handle act() automatically:

Option A: Use `waitFor` for assertions

javascript
import { render, screen, waitFor } from '@testing-library/react';

test('async update with waitFor', async () => {
  render(<AsyncComponent />);

  // waitFor automatically wraps assertions in act()
  await waitFor(() => {
    expect(screen.getByText('Data loaded')).toBeInTheDocument();
  });
});

Option B: Use `findBy` queries

javascript
test('async update with findBy', async () => {
  render(<AsyncComponent />);

  // findBy queries wait for elements to appear
  const element = await screen.findByText('Data loaded');
  expect(element).toBeInTheDocument();
});

Option C: Use `userEvent` with await

javascript
import userEvent from '@testing-library/user-event';

test('user interaction with async update', async () => {
  const user = userEvent.setup();
  render(<FormComponent />);

  // userEvent methods return promises
  await user.click(screen.getByText('Submit'));

  // State updates are handled automatically
  expect(screen.getByText('Submitted')).toBeInTheDocument();
});
4Handle promise chains properly

When dealing with promise chains, ensure all state updates are wrapped:

javascript
// ❌ Problematic - state update in .then() without act()
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    setData(data); // Warning: state update outside act()
  });

// ✅ Fixed - wrap the entire chain
await act(async () => {
  const response = await fetch('/api/data');
  const data = await response.json();
  setData(data);
});

// ✅ Alternative: extract logic and test separately
const loadData = async () => {
  const response = await fetch('/api/data');
  const data = await response.json();
  setData(data);
};

// In test:
await act(async () => {
  await loadData();
});

For complex async flows, consider extracting business logic from components to make testing easier.

5Configure test environment to suppress warnings (temporary)

While fixing tests, you might want to suppress the warning temporarily. Add to your test setup file:

javascript
// jest.setup.js or similar
import { act } from '@testing-library/react';

// Suppress act() warnings during development
const originalError = console.error;
beforeAll(() => {
  console.error = (...args) => {
    if (typeof args[0] === 'string' &&
        args[0].includes('Warning: An update to') &&
        args[0].includes('inside a test was not wrapped in act')) {
      return;
    }
    originalError.call(console, ...args);
  };
});

afterAll(() => {
  console.error = originalError;
});

Important: This is a temporary measure. Always fix the root cause by properly wrapping async operations with act().

6Verify the fix and run tests

After applying fixes, verify:

1. Warnings are gone: Run tests and check console for act() warnings
2. Tests are stable: Run tests multiple times to ensure no flakiness
3. Assertions pass: All state-dependent assertions should pass consistently

bash
# Run tests with verbose output
npm test -- --verbose

# Or with Jest directly
npx jest --verbose

If warnings persist, use React DevTools or console logs to trace which component is causing the state update outside of act().

Advanced notes

### Understanding act() Internals

The act() function does several important things:
1. Flushes effects: Processes all useEffect, useLayoutEffect, and lifecycle methods
2. Updates state: Applies all pending state updates
3. Re-renders: Triggers re-renders for affected components
4. Synchronizes: Ensures everything settles before continuing

### Common Pitfalls

Overusing act(): Don't wrap everything in act(). Only use it when:
- State updates happen asynchronously
- Effects trigger state changes
- Timers or intervals update state

Nested act() calls: Avoid nesting act() calls as it can cause confusion:

javascript
// ❌ Avoid
await act(async () => {
  await act(async () => {
    // Nested act calls
  });
});

// ✅ Better
await act(async () => {
  // Single act call covering all async operations
});

Testing Library v14+ changes: In newer versions, many async utilities automatically wrap operations in act(). Check your version:

bash
npm list @testing-library/react

### Performance Considerations

Excessive use of act() can slow down tests. For optimal performance:
1. Group related async operations in a single act() call
2. Use waitFor for assertions instead of multiple act() calls
3. Mock timers with jest.useFakeTimers() for timer-based tests

### Debugging Tips

1. Add console logs: Log before/after state updates to see timing
2. Use React DevTools: Inspect component state during tests
3. Isolate components: Test problematic components in isolation
4. Check async boundaries: Ensure all promise chains are properly awaited

Frequently asked questions

What does "This error can occur when testing async state updates without wrapping" mean?
The act() function is React's testing utility that ensures all state updates and side effects are processed before assertions are made. When you see the warning "This error can occur when testing async state updates without wrapping in act()", it means React detected state changes happening outside of an act() call during asynchronous operations. This typically occurs when: 1.
How do I fix "This error can occur when testing async state updates without wrapping"?
This warning appears in React Testing Library when asynchronous state updates occur outside of act() calls in tests. It indicates that React state changes are happening without proper test synchronization, which can lead to flaky tests and false positives. The fix involves wrapping async operations with act() or using Testing Library's async utilities.
What causes "This error can occur when testing async state updates without wrapping"?
Using async/await without wrapping in act() when state updates occur Testing components with setTimeout/setInterval that update state Event handlers that trigger asynchronous state changes
Is the "This error can occur when testing async state updates without wrapping" error serious?
It is a low-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading