diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
index c1dc8a33cceb..a8475a024aab 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
@@ -638,6 +638,61 @@ describe('ReactDOMFiberAsync', () => {
});
});
+ it('calls effect cleanup when unmounting in a transition inside StrictMode', async () => {
+ let handlerCalls = 0;
+ let cleanupCalls = 0;
+ let setShow;
+
+ function Child() {
+ React.useEffect(() => {
+ const onResize = () => {
+ handlerCalls++;
+ };
+ window.addEventListener('resize', onResize);
+ return () => {
+ cleanupCalls++;
+ window.removeEventListener('resize', onResize);
+ };
+ }, []);
+
+ return
Child
;
+ }
+
+ function App() {
+ const [show, _setShow] = React.useState(true);
+ setShow = _setShow;
+ return <>{show ? : null}>;
+ }
+
+ const root = ReactDOMClient.createRoot(container);
+ await act(async () => {
+ root.render(
+
+
+ ,
+ );
+ });
+
+ await act(async () => {
+ React.startTransition(() => {
+ setShow(false);
+ });
+ });
+
+ // Cleanup should remove the listener before any post-unmount event.
+ window.dispatchEvent(new Event('resize'));
+ expect(handlerCalls).toBe(0);
+ if (__DEV__) {
+ expect(cleanupCalls).toBe(2);
+ } else {
+ expect(cleanupCalls).toBe(1);
+ }
+
+ await act(async () => {
+ root.unmount();
+ });
+ });
+
it('Should not flush transition lanes if there is no transition scheduled in popState', async () => {
let setHasNavigated;
function App() {
diff --git a/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js b/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js
index 887ac7d1ce84..be59af513eac 100644
--- a/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js
+++ b/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js
@@ -127,6 +127,35 @@ describe('Activity StrictMode', () => {
]);
});
+ // @gate __DEV__
+ it('calls passive cleanup when unmounting in a transition', async () => {
+ const root = ReactNoop.createRoot();
+
+ await act(() => {
+ root.render(
+
+
+
+
+ ,
+ );
+ });
+
+ log = [];
+
+ await act(() => {
+ React.startTransition(() => {
+ root.render(
+
+
+ ,
+ );
+ });
+ });
+
+ expect(log).toContain('A: useEffect unmount');
+ });
+
it('should not cause infinite render loop when StrictMode is used with Suspense and synchronous set states', async () => {
// This is a regression test, see https://github.com/facebook/react/pull/25179 for more details.
function App() {