How to Mock Concurrent Mode in React
Testing React applications that utilize Concurrent Mode and features
like transitions can be difficult due to their non-blocking,
asynchronous nature. This article explains how to mock React’s
concurrent features, such as useTransition and
useDeferredValue, using Jest to make your component tests
run predictably and synchronously.
Why Mock Concurrent Mode?
Concurrent features in React (introduced in React 18) de-prioritize certain state updates to keep the main thread responsive. During a transition, React may prepare the new UI in the background while keeping the current UI visible.
In a testing environment, this non-blocking behavior can cause race conditions or assertion failures because your assertions may execute before the lower-priority background update completes. Mocking these hooks forces React to process these updates synchronously, eliminating flakiness.
Mocking useTransition
The useTransition hook returns an isPending
boolean and a startTransition function. To mock this hook
so that it runs synchronously, you can override its behavior using
Jest.
import React from 'react';
jest.mock('react', () => {
const actualReact = jest.requireActual('react');
return {
...actualReact,
useTransition: () => [false, (callback) => callback()],
};
});How It Works:
isPending(false): We hardcode this tofalsebecause the transition executes immediately.startTransition(callback => callback()): Instead of scheduling a low-priority background render, the mocked function executes the state-updating callback immediately and synchronously.
Mocking useDeferredValue
The useDeferredValue hook defers updating a part of the
UI. To mock it so that it returns the new value instantly without any
lag or deferral, use the following mock:
import React from 'react';
jest.mock('react', () => {
const actualReact = jest.requireActual('react');
return {
...actualReact,
useDeferredValue: (value) => value,
};
});How It Works:
- The mocked hook accepts the input
valueand immediately returns it, bypassing the deferral mechanism entirely.
Complete Mocking Template
If your test suite requires mocking both concurrent hooks, you can combine them into a single Jest mock block at the top of your test file:
import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import MyComponent from './MyComponent';
// Mock concurrent features globally for this test file
jest.mock('react', () => {
const actualReact = jest.requireActual('react');
return {
...actualReact,
useTransition: () => [false, (cb) => cb()],
useDeferredValue: (value) => value,
};
});
test('should render updates synchronously', () => {
render(<MyComponent />);
const button = screen.getByRole('button');
fireEvent.click(button);
// Assertions will now pass immediately without needing complex async helpers
expect(screen.getByText('Updated Content')).toBeInTheDocument();
});