Add renderToMarkup for Client Components (#30121)

Follow up to #30105.

This supports `renderToMarkup` in a non-RSC environment (not the
`react-server` condition).

This is just a Fizz renderer but it errors at runtime when you use
state, effects or event handlers that would require hydration - like the
RSC version would. (Except RSC can give early errors too.)

To do this I have to move the `react-html` builds to a new `markup`
dimension out of the `dom-legacy` dimension so that we can configure
this differently from `renderToString`/`renderToStaticMarkup`.
Eventually that dimension can go away though if deprecated. That also
helps us avoid dynamic configuration and we can just compile in the
right configuration so the split helps anyway.

One consideration is that if a compiler strips out useEffects or inlines
initial state from useState, then it would not get called an the error
wouldn't happen. Therefore to preserve semantics, a compiler would need
to inject some call that can check the current renderer and whether it
should throw.

There is an argument that it could be useful to not error for these
because it's possible to write components that works with SSR but are
just optionally hydrated. However, there's also an argument that doing
that silently is too easy to lead to mistakes and it's better to error -
especially for the e-mail use case where you can't take it back but you
can replay a queue that had failures. There are other ways to
conditionally branch components intentionally. Besides if you want it to
be silent you can still use renderToString (or better yet
renderToReadableStream).

The primary mechanism is the RSC environment and the client-environment
is really the secondary one that's only there to support legacy
environments. So this also ensures parity with the primary environment.

DiffTrain build for commit https://github.com/facebook/react/commit/1e241f9d6c5f7d0e875b19a99c83cd6197fa62f7.
This commit is contained in:
sebmarkbage
2024-06-28 13:31:00 +00:00
parent 0ee0a32085
commit 2387b495d5
14 changed files with 99 additions and 77 deletions
+1 -1
View File
@@ -1 +1 @@
19.0.0-native-fb-e02baf6c92-20240627
19.0.0-native-fb-1e241f9d6c-20240628
@@ -7,13 +7,13 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<42baca610fefec93b87cf45e12c0249d>>
* @generated SignedSource<<2af3711d9207e592f87fa05393f31919>>
*/
"use strict";
__DEV__ &&
(function () {
function JSCompiler_object_inline_createNodeMock_1106() {
function JSCompiler_object_inline_createNodeMock_1089() {
return null;
}
function findHook(fiber, id) {
@@ -4650,38 +4650,50 @@ __DEV__ &&
hook.memoizedState = [prevState, deps];
return prevState;
}
function mountDeferredValue(value) {
mountWorkInProgressHook().memoizedState = value;
return value;
function mountDeferredValue(value, initialValue) {
var hook = mountWorkInProgressHook();
return mountDeferredValueImpl(hook, value, initialValue);
}
function updateDeferredValue(value) {
function updateDeferredValue(value, initialValue) {
var hook = updateWorkInProgressHook();
return updateDeferredValueImpl(hook, currentHook.memoizedState, value);
return updateDeferredValueImpl(
hook,
currentHook.memoizedState,
value,
initialValue
);
}
function rerenderDeferredValue(value) {
function rerenderDeferredValue(value, initialValue) {
var hook = updateWorkInProgressHook();
return null === currentHook
? ((hook.memoizedState = value), value)
: updateDeferredValueImpl(hook, currentHook.memoizedState, value);
? mountDeferredValueImpl(hook, value, initialValue)
: updateDeferredValueImpl(
hook,
currentHook.memoizedState,
value,
initialValue
);
}
function updateDeferredValueImpl(hook, prevValue, value) {
function mountDeferredValueImpl(hook, value, initialValue) {
if (void 0 === initialValue || 0 !== (renderLanes & DeferredLane))
return (hook.memoizedState = value);
hook.memoizedState = initialValue;
hook = requestDeferredLane();
currentlyRenderingFiber$1.lanes |= hook;
workInProgressRootSkippedLanes |= hook;
return initialValue;
}
function updateDeferredValueImpl(hook, prevValue, value, initialValue) {
if (objectIs(value, prevValue)) return value;
if (null !== currentTreeHiddenStackCursor.current)
return (
(hook.memoizedState = value),
objectIs(value, prevValue) || (didReceiveUpdate = !0),
value
(hook = mountDeferredValueImpl(hook, value, initialValue)),
objectIs(hook, prevValue) || (didReceiveUpdate = !0),
hook
);
if (0 === (renderLanes & (SyncLane | InputContinuousLane | DefaultLane)))
return (didReceiveUpdate = !0), (hook.memoizedState = value);
0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & OffscreenLane)
? OffscreenLane
: claimNextTransitionLane());
hook = suspenseHandlerStackCursor.current;
null !== hook && (hook.flags |= 32);
hook = workInProgressDeferredLane;
hook = requestDeferredLane();
currentlyRenderingFiber$1.lanes |= hook;
workInProgressRootSkippedLanes |= hook;
return prevValue;
@@ -10585,6 +10597,16 @@ __DEV__ &&
: DefaultEventPriority;
return fiber;
}
function requestDeferredLane() {
0 === workInProgressDeferredLane &&
(workInProgressDeferredLane =
0 !== (workInProgressRootRenderLanes & OffscreenLane)
? OffscreenLane
: claimNextTransitionLane());
var suspenseHandler = suspenseHandlerStackCursor.current;
null !== suspenseHandler && (suspenseHandler.flags |= 32);
return workInProgressDeferredLane;
}
function scheduleUpdateOnFiber(root, fiber, lane) {
isRunningInsertionEffect &&
error$jscomp$0("useInsertionEffect must not schedule updates.");
@@ -13683,10 +13705,10 @@ __DEV__ &&
currentHookNameInDev = "useDebugValue";
mountHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
mountHookTypesDev();
return mountDeferredValue(value);
return mountDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -13807,10 +13829,10 @@ __DEV__ &&
currentHookNameInDev = "useDebugValue";
updateHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
updateHookTypesDev();
return mountDeferredValue(value);
return mountDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -13937,10 +13959,10 @@ __DEV__ &&
currentHookNameInDev = "useDebugValue";
updateHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
updateHookTypesDev();
return updateDeferredValue(value);
return updateDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -14062,10 +14084,10 @@ __DEV__ &&
currentHookNameInDev = "useDebugValue";
updateHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
updateHookTypesDev();
return rerenderDeferredValue(value);
return rerenderDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -14202,11 +14224,11 @@ __DEV__ &&
warnInvalidHookAccess();
mountHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
warnInvalidHookAccess();
mountHookTypesDev();
return mountDeferredValue(value);
return mountDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -14356,11 +14378,11 @@ __DEV__ &&
warnInvalidHookAccess();
updateHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
warnInvalidHookAccess();
updateHookTypesDev();
return updateDeferredValue(value);
return updateDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -14507,11 +14529,11 @@ __DEV__ &&
warnInvalidHookAccess();
updateHookTypesDev();
},
useDeferredValue: function (value) {
useDeferredValue: function (value, initialValue) {
currentHookNameInDev = "useDeferredValue";
warnInvalidHookAccess();
updateHookTypesDev();
return rerenderDeferredValue(value);
return rerenderDeferredValue(value, initialValue);
},
useTransition: function () {
currentHookNameInDev = "useTransition";
@@ -15072,20 +15094,20 @@ __DEV__ &&
scheduleRoot: scheduleRoot,
setRefreshHandler: setRefreshHandler,
getCurrentFiber: getCurrentFiberForDevTools,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
})({
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 1,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-test-renderer"
});
exports._Scheduler = Scheduler;
exports.act = act;
exports.create = function (element, options) {
var createNodeMock = JSCompiler_object_inline_createNodeMock_1106,
var createNodeMock = JSCompiler_object_inline_createNodeMock_1089,
isConcurrent = !1,
isStrictMode = !1,
concurrentUpdatesByDefault = null;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<f5de4052aa29bedfe407f3611b05a317>>
* @generated SignedSource<<a44467d77f1f00ee1ee87b0a5a8f91c2>>
*/
"use strict";
@@ -9399,7 +9399,7 @@ var devToolsConfig$jscomp$inline_1034 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1223 = {
@@ -9430,7 +9430,7 @@ var internals$jscomp$inline_1223 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1224 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<af4ea00784664da9aaa9bc4db9dca3f0>>
* @generated SignedSource<<bc5a7bb7c33a13fe5d439be4ae4fd382>>
*/
"use strict";
@@ -10019,7 +10019,7 @@ var devToolsConfig$jscomp$inline_1117 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-test-renderer"
};
(function (internals) {
@@ -10063,7 +10063,7 @@ var devToolsConfig$jscomp$inline_1117 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
exports._Scheduler = Scheduler;
exports.act = act;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<547bd318a581226337229e507fdff7e4>>
* @generated SignedSource<<131adf5c737c8b49598bfde63510802f>>
*/
"use strict";
@@ -1741,7 +1741,7 @@ __DEV__ &&
exports.useTransition = function () {
return resolveDispatcher().useTransition();
};
exports.version = "19.0.0-native-fb-e02baf6c92-20240627";
exports.version = "19.0.0-native-fb-1e241f9d6c-20240628";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<f500c5a68996ef417fddaed92745aa08>>
* @generated SignedSource<<25ae1bcff2252ad936da7c12d576c871>>
*/
"use strict";
@@ -604,4 +604,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-native-fb-e02baf6c92-20240627";
exports.version = "19.0.0-native-fb-1e241f9d6c-20240628";
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<8fed01692ebeb6102c3169cea0452442>>
* @generated SignedSource<<1d8f6af1192334e3b89f47a722651256>>
*/
"use strict";
@@ -608,7 +608,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-native-fb-e02baf6c92-20240627";
exports.version = "19.0.0-native-fb-1e241f9d6c-20240628";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
@@ -1 +1 @@
e02baf6c92833a0d45a77fb2e741676f393c24f7
1e241f9d6c5f7d0e875b19a99c83cd6197fa62f7
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<362f23653ecadf010c8628af12b5d28b>>
* @generated SignedSource<<1aa7924f699333039f1024464df04c53>>
*/
"use strict";
@@ -16679,12 +16679,12 @@ __DEV__ &&
scheduleRoot: scheduleRoot,
setRefreshHandler: setRefreshHandler,
getCurrentFiber: getCurrentFiberForDevTools,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
})({
findFiberByHostInstance: getInstanceFromNode,
bundleType: 1,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<a6e7219b8f48de58f7031404726e00d2>>
* @generated SignedSource<<b230f5546087f912d3c699f272c9d4b9>>
*/
"use strict";
@@ -10594,7 +10594,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1136 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10637,7 +10637,7 @@ var internals$jscomp$inline_1362 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1363 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<3b22c4c9bbdc85d394760e31e09a8df9>>
* @generated SignedSource<<bc0cc2c8d143b20430b374a8ed26e181>>
*/
"use strict";
@@ -11300,7 +11300,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1217 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11356,7 +11356,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
exports.createPortal = function (children, containerTag) {
return createPortal$1(
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<d8440b40e936886834ad4d01de6ab9ff>>
* @generated SignedSource<<027863d09f9bb09cadf17bac10054b17>>
*/
"use strict";
@@ -16867,11 +16867,11 @@ __DEV__ &&
var emptyObject = {};
Object.freeze(emptyObject);
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-native-fb-e02baf6c92-20240627" !== isomorphicReactPackageVersion)
if ("19.0.0-native-fb-1e241f9d6c-20240628" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-native-fb-e02baf6c92-20240627\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-native-fb-1e241f9d6c-20240628\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -16919,12 +16919,12 @@ __DEV__ &&
scheduleRoot: scheduleRoot,
setRefreshHandler: setRefreshHandler,
getCurrentFiber: getCurrentFiberForDevTools,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
})({
findFiberByHostInstance: getInstanceFromTag,
bundleType: 1,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<45f2decc15fefdff8d8b2d1c52772351>>
* @generated SignedSource<<86413c46d75732302a2eaee0e0b0f194>>
*/
"use strict";
@@ -10748,11 +10748,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-native-fb-e02baf6c92-20240627" !== isomorphicReactPackageVersion)
if ("19.0.0-native-fb-1e241f9d6c-20240628" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-native-fb-e02baf6c92-20240627\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-native-fb-1e241f9d6c-20240628\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -10802,7 +10802,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1210 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10845,7 +10845,7 @@ var internals$jscomp$inline_1457 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1458 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<a3cc27cbbc34d3cb35eb45fbd471678a>>
* @generated SignedSource<<393a5cf58fd413125532826db88447b3>>
*/
"use strict";
@@ -11455,11 +11455,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-native-fb-e02baf6c92-20240627" !== isomorphicReactPackageVersion)
if ("19.0.0-native-fb-1e241f9d6c-20240628" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-native-fb-e02baf6c92-20240627\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-native-fb-1e241f9d6c-20240628\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -11509,7 +11509,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1291 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-native-fb-e02baf6c92-20240627",
version: "19.0.0-native-fb-1e241f9d6c-20240628",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11565,7 +11565,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-e02baf6c92-20240627"
reconcilerVersion: "19.0.0-native-fb-1e241f9d6c-20240628"
});
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
computeComponentStackForErrorReporting: function (reactTag) {