Repository: facebook/react
Pull request: Test bad useEffect return value with noop-renderer

## Summary

Tests introduced in https://github.com/facebook/react/pull/14069 now use the noop renderer. This would've caught that `react-dom` doesn't actually trigger the warnings in React 17 when using `useEffect` (https://codesandbox.io/s/effect-destroy-function-type-izslc?file=/src/App.js)

Related: https://github.com/reactwg/react-18/discussions/95 

## How did you test this change?

Backported the proposed test to `17.0.2` (`git checkout 17.0.2`), ran `yarn test ReactHooksWithNoopRenderer`. When `17.0.2` was checked out, the test failed. However, it's passing i.e. warnings are logged as expected on `HEAD`.


Changed files:
filename: packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
patch: @@ -727,42 +727,6 @@ describe('ReactHooks', () => {
     ReactTestRenderer.create(<App deps={undefined} />);
   });
 
-  it('assumes useEffect clean-up function is either a function or undefined', () => {
-    const {useLayoutEffect} = React;
-
-    function App(props) {
-      useLayoutEffect(() => {
-        return props.return;
-      });
-      return null;
-    }
-
-    const root1 = ReactTestRenderer.create(null);
-    expect(() => root1.update(<App return={17} />)).toErrorDev([
-      'Warning: An effect function must not return anything besides a ' +
-        'function, which is used for clean-up. You returned: 17',
-    ]);
-
-    const root2 = ReactTestRenderer.create(null);
-    expect(() => root2.update(<App return={null} />)).toErrorDev([
-      'Warning: An effect function must not return anything besides a ' +
-        'function, which is used for clean-up. You returned null. If your ' +
-        'effect does not require clean up, return undefined (or nothing).',
-    ]);
-
-    const root3 = ReactTestRenderer.create(null);
-    expect(() => root3.update(<App return={Promise.resolve()} />)).toErrorDev([
-      'Warning: An effect function must not return anything besides a ' +
-        'function, which is used for clean-up.\n\n' +
-        'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
-    ]);
-
-    // Error on unmount because React assumes the value is a function
-    expect(() => {
-      root3.update(null);
-    }).toThrow('is not a function');
-  });
-
   it('does not forget render phase useState updates inside an effect', () => {
     const {useState, useEffect} = React;
 

filename: packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
patch: @@ -2632,6 +2632,54 @@ describe('ReactHooksWithNoopRenderer', () => {
       });
       expect(Scheduler).toHaveYielded(['layout destroy', 'passive destroy']);
     });
+
+    it('assumes passive effect destroy function is either a function or undefined', () => {
+      function App(props) {
+        useEffect(() => {
+          return props.return;
+        });
+        return null;
+      }
+
+      const root1 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root1.render(<App return={17} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up. You returned: 17',
+      ]);
+
+      const root2 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root2.render(<App return={null} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up. You returned null. If your ' +
+          'effect does not require clean up, return undefined (or nothing).',
+      ]);
+
+      const root3 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root3.render(<App return={Promise.resolve()} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up.\n\n' +
+          'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
+      ]);
+
+      // Error on unmount because React assumes the value is a function
+      expect(() =>
+        act(() => {
+          root3.unmount();
+        }),
+      ).toThrow('is not a function');
+    });
   });
 
   describe('useLayoutEffect', () => {
@@ -2810,6 +2858,54 @@ describe('ReactHooksWithNoopRenderer', () => {
       ]);
       expect(ReactNoop.getChildren()).toEqual([span('OuterFallback')]);
     });
+
+    it('assumes layout effect destroy function is either a function or undefined', () => {
+      function App(props) {
+        useLayoutEffect(() => {
+          return props.return;
+        });
+        return null;
+      }
+
+      const root1 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root1.render(<App return={17} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up. You returned: 17',
+      ]);
+
+      const root2 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root2.render(<App return={null} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up. You returned null. If your ' +
+          'effect does not require clean up, return undefined (or nothing).',
+      ]);
+
+      const root3 = ReactNoop.createRoot();
+      expect(() =>
+        act(() => {
+          root3.render(<App return={Promise.resolve()} />);
+        }),
+      ).toErrorDev([
+        'Warning: An effect function must not return anything besides a ' +
+          'function, which is used for clean-up.\n\n' +
+          'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
+      ]);
+
+      // Error on unmount because React assumes the value is a function
+      expect(() =>
+        act(() => {
+          root3.unmount();
+        }),
+      ).toThrow('is not a function');
+    });
   });
 
   describe('useCallback', () => {