Repository: facebook/react
Pull request: Fix useResourceEffect in Fizz

We're seeing errors when testing useResourceEffect in SSR and it turns out we're missing the noop dispatcher function on Fizz.

I tested a local build with this change and it resolved the late mutation errors in the e2e tests.

Changed files:
filename: packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
patch: @@ -27,6 +27,7 @@ let useRef;
 let useImperativeHandle;
 let useInsertionEffect;
 let useLayoutEffect;
+let useResourceEffect;
 let useDebugValue;
 let forwardRef;
 let yieldedValues;
@@ -51,6 +52,7 @@ function initModules() {
   useImperativeHandle = React.useImperativeHandle;
   useInsertionEffect = React.useInsertionEffect;
   useLayoutEffect = React.useLayoutEffect;
+  useResourceEffect = React.experimental_useResourceEffect;
   forwardRef = React.forwardRef;
 
   yieldedValues = [];
@@ -653,6 +655,52 @@ describe('ReactDOMServerHooks', () => {
     });
   });
 
+  describe('useResourceEffect', () => {
+    gate(flags => {
+      if (flags.enableUseResourceEffectHook) {
+        const yields = [];
+        itRenders(
+          'should ignore resource effects on the server',
+          async render => {
+            function Counter(props) {
+              useResourceEffect(
+                () => {
+                  yieldValue('created on client');
+                  return {resource_counter: props.count};
+                },
+                [props.count],
+                resource => {
+                  resource.resource_counter = props.count;
+                  yieldValue('updated on client');
+                },
+                [props.count],
+                () => {
+                  yieldValue('cleanup on client');
+                },
+              );
+              return <Text text={'Count: ' + props.count} />;
+            }
+
+            const domNode = await render(<Counter count={0} />);
+            yields.push(clearLog());
+            expect(domNode.tagName).toEqual('SPAN');
+            expect(domNode.textContent).toEqual('Count: 0');
+          },
+        );
+
+        it('verifies yields in order', () => {
+          expect(yields).toEqual([
+            ['Count: 0'], // server render
+            ['Count: 0'], // server stream
+            ['Count: 0', 'created on client'], // clean render
+            ['Count: 0', 'created on client'], // hydrated render
+            // nothing yielded for bad markup
+          ]);
+        });
+      }
+    });
+  });
+
   describe('useContext', () => {
     itThrowsWhenRendering(
       'if used inside a class component',

filename: packages/react-server/src/ReactFizzHooks.js
patch: @@ -43,6 +43,7 @@ import {
   enableUseEffectEventHook,
   enableUseMemoCacheHook,
   enableAsyncActions,
+  enableUseResourceEffectHook,
 } from 'shared/ReactFeatureFlags';
 import is from 'shared/objectIs';
 import {
@@ -870,6 +871,11 @@ if (enableAsyncActions) {
   HooksDispatcher.useFormState = useActionState;
   HooksDispatcher.useActionState = useActionState;
 }
+if (enableUseResourceEffectHook) {
+  HooksDispatcher.useResourceEffect = supportsClientAPIs
+    ? noop
+    : clientHookNotSupported;
+}
 
 export let currentResumableState: null | ResumableState = (null: any);
 export function setCurrentResumableState(