How to Test React Strict Mode
React Strict Mode is an essential development tool that helps identify potential issues in an application by intentionally double-rendering components and double-invoking effects. To ensure your components are resilient to these behaviors, you must write tests that execute within this strict environment. This article provides a straightforward guide on how to configure your testing environment using Jest and React Testing Library to verify that your components function correctly under React Strict Mode.
Enabling Strict Mode in Tests
To test how a component behaves in Strict Mode, you must wrap the
component under test inside the <React.StrictMode>
component. React Testing Library renders components exactly as they
would run in the browser, meaning this wrapper will trigger Strict
Mode’s double-rendering and double-effect-firing behaviors during your
tests.
Here is a basic setup using React Testing Library and Jest:
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
test('renders correctly under Strict Mode', () => {
render(
<React.StrictMode>
<MyComponent />
</React.StrictMode>
);
// Assert that the component behaves as expected
expect(screen.getByRole('heading')).toHaveTextContent('Hello World');
});Testing for Double-Mounting and Cleanup Issues
In React 18 and later, Strict Mode mounts, unmounts, and mounts your
components again on initial render. This is designed to expose missing
cleanup functions in useEffect hooks. If your component
fails to clean up subscriptions, event listeners, or timers, this
double-mount behavior will trigger bugs during your tests.
Example: Testing Event Listeners
If a component registers a global event listener but forgets to clean it up, the double-mount in Strict Mode will register the listener twice. You can test and catch this behavior by spying on the global window object.
import React, { useEffect } from 'react';
import { render } from '@testing-library/react';
// Component under test
function EventComponent() {
useEffect(() => {
const handleResize = () => {};
window.addEventListener('resize', handleResize);
// Cleanup function (if omitted, the test below will catch the duplicate listener)
return () => window.removeEventListener('resize', handleResize);
}, []);
return <div>Event Component</div>;
}
test('does not duplicate event listeners in Strict Mode', () => {
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
render(
<React.StrictMode>
<EventComponent />
</React.StrictMode>
);
// Strict Mode mounts (1), unmounts (2), and mounts again (3)
// Total additions should match total removals plus the active one
expect(addEventListenerSpy).toHaveBeenCalledTimes(2);
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
addEventListenerSpy.mockRestore();
removeEventListenerSpy.mockRestore();
});Catching Deprecation and Strict Mode Warnings
React Strict Mode warns you about unsafe lifecycles, legacy API usage
(like findDOMNode), and deprecated context APIs by printing
warnings to console.error. To ensure your codebase remains
compliant, you can write a test that fails if any console errors or
warnings are detected during rendering.
describe('Strict Mode Compliance', () => {
let consoleErrorSpy;
beforeAll(() => {
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation((message) => {
// Fail the test if a Strict Mode warning is logged
if (message.includes('Warning:')) {
throw new Error(`React Warning detected in Strict Mode: ${message}`);
}
});
});
afterAll(() => {
consoleErrorSpy.mockRestore();
});
test('should not produce any React console warnings', () => {
render(
<React.StrictMode>
<MyComponent />
</React.StrictMode>
);
});
});By wrapping your components in <React.StrictMode>
during testing and monitoring both component lifecycles and console
outputs, you can prevent memory leaks and deprecation issues from
reaching production.