diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
index e1aeea70fd806..b79e59ad004dd 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
@@ -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 ;
+ }
+
+ const domNode = await render();
+ 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',
diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js
index 78e114e373e6b..84b9448ca32f6 100644
--- a/packages/react-server/src/ReactFizzHooks.js
+++ b/packages/react-server/src/ReactFizzHooks.js
@@ -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(