React version: 19.1.0
Steps To Reproduce
- Enable React.StrictMode in the root of the app
- Create a component with a useEffect that sets up an event listener and returns a cleanup function
- Use concurrent features (startTransition) to unmount the component
- Observe that the cleanup function is not called on unmount in some edge cases
Link to code example:
import { useEffect, startTransition, useState } from 'react';
function MyComponent() {
useEffect(() => {
const handler = () => console.log('event fired');
window.addEventListener('resize', handler);
return () => {
console.log('cleanup called'); // This does not log on unmount
window.removeEventListener('resize', handler);
};
}, []);
return <div>Hello</div>;
}
The current behavior
The cleanup function is not reliably called when the component unmounts inside StrictMode with concurrent rendering, leading to memory leaks and duplicate event listeners.
The expected behavior
The cleanup function returned from useEffect should always be called when the component unmounts, regardless of whether StrictMode or concurrent features are active.
React version: 19.1.0
Steps To Reproduce
Link to code example:
The current behavior
The cleanup function is not reliably called when the component unmounts inside StrictMode with concurrent rendering, leading to memory leaks and duplicate event listeners.
The expected behavior
The cleanup function returned from useEffect should always be called when the component unmounts, regardless of whether StrictMode or concurrent features are active.