Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suspend on uncaught error, if inside transition #23266

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
return NoopRenderer.flushSync(fn);
}

function onRecoverableError(error) {
function onRecoverableErrorDefault(error) {
// TODO: Turn this on once tests are fixed
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
// console.error(error);
Expand Down Expand Up @@ -972,15 +972,15 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
null,
false,
'',
onRecoverableError,
onRecoverableErrorDefault,
);
roots.set(rootID, root);
}
return root.current.stateNode.containerInfo;
},

// TODO: Replace ReactNoop.render with createRoot + root.render
createRoot() {
createRoot(options) {
const container = {
rootID: '' + idCounter++,
pendingChildren: [],
Expand All @@ -994,8 +994,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
null,
false,
'',
onRecoverableError,
options && options.onRecoverableError
? options.onRecoverableError
: onRecoverableErrorDefault,
);

return {
_Scheduler: Scheduler,
render(children: ReactNodeList) {
Expand Down Expand Up @@ -1024,7 +1027,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
null,
false,
'',
onRecoverableError,
onRecoverableErrorDefault,
);
return {
_Scheduler: Scheduler,
Expand Down
26 changes: 19 additions & 7 deletions packages/react-reconciler/src/ReactFiberThrow.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import {
} from './ReactFiberSuspenseContext.new';
import {
renderDidError,
renderDidErrorUncaught,
queueConcurrentError,
onUncaughtError,
markLegacyErrorBoundaryAsFailed,
isAlreadyFailedLegacyErrorBoundary,
Expand Down Expand Up @@ -516,22 +518,32 @@ function throwException(
queueHydrationError(value);
return;
}
} else {
// Otherwise, fall through to the error path.
}

// Otherwise, fall through to the error path.

// Push the error to a queue. If we end up recovering without surfacing
// the error to the user, we'll updgrade this to a recoverable error and
// log it with onRecoverableError.
//
// This is intentionally a separate call from renderDidError because in
// some cases we use the error handling path as an implementation detail
// to unwind the stack, but we don't want to log it as a real error. An
// example is suspending outside of a Suspense boundary (see previous
// branch above).
queueConcurrentError(value);
}

// We didn't find a boundary that could handle this type of exception. Start
// over and traverse parent path again, this time treating the exception
// as an error.
renderDidError(value);

value = createCapturedValue(value, sourceFiber);
const error = value;
const errorInfo = createCapturedValue(error, sourceFiber);
let workInProgress = returnFiber;
do {
switch (workInProgress.tag) {
case HostRoot: {
const errorInfo = value;
renderDidErrorUncaught();
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
Expand All @@ -541,7 +553,7 @@ function throwException(
}
case ClassComponent:
// Capture and retry
const errorInfo = value;
renderDidError();
const ctor = workInProgress.type;
const instance = workInProgress.stateNode;
if (
Expand Down
26 changes: 19 additions & 7 deletions packages/react-reconciler/src/ReactFiberThrow.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import {
} from './ReactFiberSuspenseContext.old';
import {
renderDidError,
renderDidErrorUncaught,
queueConcurrentError,
onUncaughtError,
markLegacyErrorBoundaryAsFailed,
isAlreadyFailedLegacyErrorBoundary,
Expand Down Expand Up @@ -516,22 +518,32 @@ function throwException(
queueHydrationError(value);
return;
}
} else {
// Otherwise, fall through to the error path.
}

// Otherwise, fall through to the error path.

// Push the error to a queue. If we end up recovering without surfacing
// the error to the user, we'll updgrade this to a recoverable error and
// log it with onRecoverableError.
//
// This is intentionally a separate call from renderDidError because in
// some cases we use the error handling path as an implementation detail
// to unwind the stack, but we don't want to log it as a real error. An
// example is suspending outside of a Suspense boundary (see previous
// branch above).
queueConcurrentError(value);
}

// We didn't find a boundary that could handle this type of exception. Start
// over and traverse parent path again, this time treating the exception
// as an error.
renderDidError(value);

value = createCapturedValue(value, sourceFiber);
const error = value;
const errorInfo = createCapturedValue(error, sourceFiber);
let workInProgress = returnFiber;
do {
switch (workInProgress.tag) {
case HostRoot: {
const errorInfo = value;
renderDidErrorUncaught();
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
Expand All @@ -541,7 +553,7 @@ function throwException(
}
case ClassComponent:
// Capture and retry
const errorInfo = value;
renderDidError();
const ctor = workInProgress.type;
const instance = workInProgress.stateNode;
if (
Expand Down
Loading