From 3e88b76c54b0f590ed9bec31001a2b024f3b71c1 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 13 Dec 2024 10:36:39 -0500 Subject: [PATCH 1/2] Add useResourceEffect to the HooksDispatcher for Fizz --- packages/react-server/src/ReactFizzHooks.js | 6 ++++++ 1 file changed, 6 insertions(+) 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( From c54364c6ab2c6e9c252b1ee14e900a2431ae1868 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 13 Dec 2024 11:10:58 -0500 Subject: [PATCH 2/2] Add test for useResourceEffect in SSR --- .../ReactDOMServerIntegrationHooks-test.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) 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',