Skip to content

Commit

Permalink
[Fiber] Create virtual Fiber when an error occurs during reconcilation (
Browse files Browse the repository at this point in the history
#29804)

This lets us rethrow it in the conceptual place of the child.

There's currently a problem when we suspend or throw in the child fiber
reconciliation phase. This work is done by the parent component, so if
it suspends or errors it is as if that component errored or suspended.
However, conceptually it's like a child suspended or errored.

In theory any thing can throw but it is really mainly due to either
`React.lazy` (both in the element.type position and node position),
`Thenable`s or the `Thenable`s that make up `AsyncIterable`s.

Mainly this happens because a Server Component that errors turns into a
`React.lazy`. In practice this means that if you have a Server Component
as the direct child of an Error Boundary. Errors inside of it won't be
caught.

We used to have the same problem with Thenables and Suspense but because
it's now always nested inside an inner Offscreen boundary that shields
it by being one level nested. However, when we have raw Offscreen
(Activity) boundaries they should also be able to catch the suspense if
it's in a hidden state so the problem returns. This fixes it for thrown
promises but it doesn't fix it for SuspenseException. I'm not sure this
is even the right strategy for Suspense though. It kind of relies on the
node never actually mounting/committing.

It's conceptually a little tricky because the current component can
inspect the children and make decisions based on them. Such as
SuspenseList.

The other thing that this PR tries to address is that it sets the
foundation for dealing with error reporting for Server Components that
errored. If something client side errors it'll be a stack like Server
(DebugInfo) -> Fiber -> Fiber -> Server -> (DebugInfo) -> Fiber.
However, all error reporting relies on it eventually terminating into a
Fiber that is responsible for the error. To avoid having to fork too
much it would be nice if I could create a Fiber to associate with the
error so that even a Server component error in this case ultimately
terminates in a Fiber.

DiffTrain build for commit 270229f.
  • Loading branch information
sebmarkbage committed Jun 11, 2024
1 parent 810dff6 commit 39d07c2
Show file tree
Hide file tree
Showing 14 changed files with 429 additions and 252 deletions.
2 changes: 1 addition & 1 deletion compiled-rn/VERSION_NATIVE_FB
Original file line number Diff line number Diff line change
@@ -1 +1 @@
19.0.0-native-fb-2774208039-20240610
19.0.0-native-fb-270229f0c3-20240611
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<e9c8f9f36a45a6dd3249367e7f40a6db>>
* @generated SignedSource<<82e2b14aefb0095e8e550c733d58de3f>>
*/

"use strict";
__DEV__ &&
(function () {
function JSCompiler_object_inline_createNodeMock_1115() {
function JSCompiler_object_inline_createNodeMock_1120() {
return null;
}
function findHook(fiber, id) {
Expand Down Expand Up @@ -3464,16 +3464,31 @@ __DEV__ &&
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
return function (returnFiber, currentFirstChild, newChild, lanes) {
thenableIndexCounter$1 = 0;
returnFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes,
null
);
thenableState$1 = null;
return returnFiber;
try {
thenableIndexCounter$1 = 0;
var firstChildFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes,
null
);
thenableState$1 = null;
return firstChildFiber;
} catch (x) {
if (
x === SuspenseException ||
(0 === (returnFiber.mode & 1) &&
"object" === typeof x &&
null !== x &&
"function" === typeof x.then)
)
throw x;
currentFirstChild = createFiber(29, x, null, returnFiber.mode);
currentFirstChild.lanes = lanes;
currentFirstChild.return = returnFiber;
return currentFirstChild;
}
};
}
function pushHiddenContext(fiber, context) {
Expand Down Expand Up @@ -7700,6 +7715,8 @@ __DEV__ &&
),
workInProgress.child
);
case 29:
throw workInProgress.pendingProps;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -8369,6 +8386,8 @@ __DEV__ &&
);
case 25:
return null;
case 29:
return null;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -15042,20 +15061,20 @@ __DEV__ &&
scheduleRoot: scheduleRoot,
setRefreshHandler: setRefreshHandler,
getCurrentFiber: getCurrentFiberForDevTools,
reconcilerVersion: "19.0.0-native-fb-2774208039-20240610"
reconcilerVersion: "19.0.0-native-fb-270229f0c3-20240611"
});
})({
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 1,
version: "19.0.0-native-fb-2774208039-20240610",
version: "19.0.0-native-fb-270229f0c3-20240611",
rendererPackageName: "react-test-renderer"
});
exports._Scheduler = Scheduler;
exports.act = act;
exports.create = function (element, options) {
var createNodeMock = JSCompiler_object_inline_createNodeMock_1115,
var createNodeMock = JSCompiler_object_inline_createNodeMock_1120,
isConcurrent = !1,
isStrictMode = !1,
concurrentUpdatesByDefault = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<db4dd88f7d1228b66c0f9b5647536e42>>
* @generated SignedSource<<adf999d3185fc19ce81dca5007d5bb72>>
*/

"use strict";
Expand Down Expand Up @@ -2213,15 +2213,31 @@ function createChildReconciler(shouldTrackSideEffects) {
: deleteRemainingChildren(returnFiber, currentFirstChild);
}
return function (returnFiber, currentFirstChild, newChild, lanes) {
thenableIndexCounter$1 = 0;
returnFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes
);
thenableState$1 = null;
return returnFiber;
try {
thenableIndexCounter$1 = 0;
var firstChildFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes,
null
);
thenableState$1 = null;
return firstChildFiber;
} catch (x) {
if (
x === SuspenseException ||
(0 === (returnFiber.mode & 1) &&
"object" === typeof x &&
null !== x &&
"function" === typeof x.then)
)
throw x;
currentFirstChild = createFiber(29, x, null, returnFiber.mode);
currentFirstChild.lanes = lanes;
currentFirstChild.return = returnFiber;
return currentFirstChild;
}
};
}
var reconcileChildFibers = createChildReconciler(!0),
Expand Down Expand Up @@ -5461,6 +5477,8 @@ function beginWork(current, workInProgress, renderLanes) {
),
workInProgress.child
);
case 29:
throw workInProgress.pendingProps;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -6037,6 +6055,8 @@ function completeWork(current, workInProgress, renderLanes) {
);
case 25:
return null;
case 29:
return null;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -9331,19 +9351,19 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1048 = {
var devToolsConfig$jscomp$inline_1054 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-2774208039-20240610",
version: "19.0.0-native-fb-270229f0c3-20240611",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1235 = {
bundleType: devToolsConfig$jscomp$inline_1048.bundleType,
version: devToolsConfig$jscomp$inline_1048.version,
rendererPackageName: devToolsConfig$jscomp$inline_1048.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1048.rendererConfig,
var internals$jscomp$inline_1241 = {
bundleType: devToolsConfig$jscomp$inline_1054.bundleType,
version: devToolsConfig$jscomp$inline_1054.version,
rendererPackageName: devToolsConfig$jscomp$inline_1054.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1054.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -9360,26 +9380,26 @@ var internals$jscomp$inline_1235 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1048.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1054.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-2774208039-20240610"
reconcilerVersion: "19.0.0-native-fb-270229f0c3-20240611"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1236 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1242 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1236.isDisabled &&
hook$jscomp$inline_1236.supportsFiber
!hook$jscomp$inline_1242.isDisabled &&
hook$jscomp$inline_1242.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1236.inject(
internals$jscomp$inline_1235
(rendererID = hook$jscomp$inline_1242.inject(
internals$jscomp$inline_1241
)),
(injectedHook = hook$jscomp$inline_1236);
(injectedHook = hook$jscomp$inline_1242);
} catch (err) {}
}
exports._Scheduler = Scheduler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<ee84d20386606858c48dfc7bb7fcd98b>>
* @generated SignedSource<<c022c8ceea256c9067b54e586075c7b9>>
*/

"use strict";
Expand Down Expand Up @@ -2301,15 +2301,31 @@ function createChildReconciler(shouldTrackSideEffects) {
: deleteRemainingChildren(returnFiber, currentFirstChild);
}
return function (returnFiber, currentFirstChild, newChild, lanes) {
thenableIndexCounter$1 = 0;
returnFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes
);
thenableState$1 = null;
return returnFiber;
try {
thenableIndexCounter$1 = 0;
var firstChildFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes,
null
);
thenableState$1 = null;
return firstChildFiber;
} catch (x) {
if (
x === SuspenseException ||
(0 === (returnFiber.mode & 1) &&
"object" === typeof x &&
null !== x &&
"function" === typeof x.then)
)
throw x;
currentFirstChild = createFiber(29, x, null, returnFiber.mode);
currentFirstChild.lanes = lanes;
currentFirstChild.return = returnFiber;
return currentFirstChild;
}
};
}
var reconcileChildFibers = createChildReconciler(!0),
Expand Down Expand Up @@ -5656,6 +5672,8 @@ function beginWork(current, workInProgress, renderLanes) {
),
workInProgress.child
);
case 29:
throw workInProgress.pendingProps;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -6283,6 +6301,8 @@ function completeWork(current, workInProgress, renderLanes) {
);
case 25:
return null;
case 29:
return null;
}
throw Error(
"Unknown unit of work tag (" +
Expand Down Expand Up @@ -9953,12 +9973,12 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1131 = {
var devToolsConfig$jscomp$inline_1137 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-2774208039-20240610",
version: "19.0.0-native-fb-270229f0c3-20240611",
rendererPackageName: "react-test-renderer"
};
(function (internals) {
Expand All @@ -9975,10 +9995,10 @@ var devToolsConfig$jscomp$inline_1131 = {
} catch (err) {}
return hook.checkDCE ? !0 : !1;
})({
bundleType: devToolsConfig$jscomp$inline_1131.bundleType,
version: devToolsConfig$jscomp$inline_1131.version,
rendererPackageName: devToolsConfig$jscomp$inline_1131.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1131.rendererConfig,
bundleType: devToolsConfig$jscomp$inline_1137.bundleType,
version: devToolsConfig$jscomp$inline_1137.version,
rendererPackageName: devToolsConfig$jscomp$inline_1137.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1137.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -9995,14 +10015,14 @@ var devToolsConfig$jscomp$inline_1131 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1131.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1137.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-2774208039-20240610"
reconcilerVersion: "19.0.0-native-fb-270229f0c3-20240611"
});
exports._Scheduler = Scheduler;
exports.act = act;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<4e0e4b457140b7f25071b271f8f187d2>>
* @generated SignedSource<<3769783c44fb588b1378fed5a4c74036>>
*/

"use strict";
Expand Down Expand Up @@ -1741,7 +1741,7 @@ __DEV__ &&
exports.useTransition = function () {
return resolveDispatcher().useTransition();
};
exports.version = "19.0.0-native-fb-2774208039-20240610";
exports.version = "19.0.0-native-fb-270229f0c3-20240611";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<138668c29d55fc3ae4dbdcec9581ff7b>>
* @generated SignedSource<<bdfc272a51011797bd56d51cde162af2>>
*/

"use strict";
Expand Down Expand Up @@ -604,4 +604,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-native-fb-2774208039-20240610";
exports.version = "19.0.0-native-fb-270229f0c3-20240611";
Loading

0 comments on commit 39d07c2

Please sign in to comment.