138
RecoverableException,
139
createFatalRecoverableError,
140
getSuspendedRecoverableError,
141
- clearSuspendedRecoverableError,
141
} from './ReactFizzHooks';
142
import {DefaultAsyncDispatcher} from './ReactFizzAsyncDispatcher';
143
import {
414
// The return string is used in production primarily to avoid leaking internals, secondarily to save bytes.
415
// Returning null/undefined will cause a default error message in production
416
onError: (error: mixed, errorInfo: ThrownInfo) => ?string,
417
+ // onBrowserBailout is called when Fizz recovers by intentionally deferring
418
+ // rendering to the browser.
419
+ onBrowserBailout: (error: mixed, errorInfo: ThrownInfo) => void,
420
// onAllReady is called when all pending task is done but it may not have flushed yet.
421
// This is a good time to start writing if you want only HTML and no intermediate steps.
422
onAllReady: () => void,
538
rootFormatContext: FormatContext,
539
progressiveChunkSize: void | number,
540
onError: void | ((error: mixed, errorInfo: ErrorInfo) => ?string),
541
+ onBrowserBailout: void | ((error: mixed, errorInfo: ErrorInfo) => void),
542
onAllReady: void | (() => void),
543
onShellReady: void | (() => void),
544
onShellError: void | ((error: mixed) => void),
575
this.trackedPostpones = null;
576
this.postponedState = null;
577
this.onError = onError === undefined ? defaultErrorHandler : onError;
578
+ this.onBrowserBailout =
579
+ onBrowserBailout === undefined ? noop : onBrowserBailout;
580
this.onAllReady = onAllReady === undefined ? noop : onAllReady;
581
this.onShellReady = onShellReady === undefined ? noop : onShellReady;
582
this.onShellError = onShellError === undefined ? noop : onShellError;
594
rootFormatContext: FormatContext,
595
progressiveChunkSize: void | number,
596
onError: void | ((error: mixed, errorInfo: ErrorInfo) => ?string),
597
+ onBrowserBailout: void | ((error: mixed, errorInfo: ErrorInfo) => void),
598
onAllReady: void | (() => void),
599
onShellReady: void | (() => void),
600
onShellError: void | ((error: mixed) => void),
612
rootFormatContext,
613
progressiveChunkSize,
614
onError,
615
+ onBrowserBailout,
616
onAllReady,
617
onShellReady,
618
onShellError,
663
rootFormatContext: FormatContext,
664
progressiveChunkSize: void | number,
665
onError: void | ((error: mixed, errorInfo: ErrorInfo) => ?string),
666
+ onBrowserBailout: void | ((error: mixed, errorInfo: ErrorInfo) => void),
667
onAllReady: void | (() => void),
668
onShellReady: void | (() => void),
669
onShellError: void | ((error: mixed) => void),
676
rootFormatContext,
677
progressiveChunkSize,
678
onError,
679
+ onBrowserBailout,
680
onAllReady,
681
onShellReady,
682
onShellError,
697
postponedState: PostponedState,
698
renderState: RenderState,
699
onError: void | ((error: mixed, errorInfo: ErrorInfo) => ?string),
700
+ onBrowserBailout: void | ((error: mixed, errorInfo: ErrorInfo) => void),
701
onAllReady: void | (() => void),
702
onShellReady: void | (() => void),
703
onShellError: void | ((error: mixed) => void),
714
postponedState.rootFormatContext,
715
postponedState.progressiveChunkSize,
716
onError,
717
+ onBrowserBailout,
718
onAllReady,
719
onShellReady,
720
onShellError,
793
postponedState: PostponedState,
794
renderState: RenderState,
795
onError: void | ((error: mixed, errorInfo: ErrorInfo) => ?string),
796
+ onBrowserBailout: void | ((error: mixed, errorInfo: ErrorInfo) => void),
797
onAllReady: void | (() => void),
798
onShellReady: void | (() => void),
799
onShellError: void | ((error: mixed) => void),
804
postponedState,
805
renderState,
806
onError,
807
+ onBrowserBailout,
808
onAllReady,
809
onShellReady,
810
onShellError,
1376
debugTask: null | ConsoleTask,
1377
): ?string {
1378
if (error === RecoverableException) {
1366
- clearSuspendedRecoverableError();
1379
+ // The fatal wrapper was created eagerly to capture the use() call site, but
1380
+ // this path recovered at a Suspense boundary. Report its original cause and
1381
+ // discard the wrapper.
1382
+ const fatalRecoverableError = getSuspendedRecoverableError();
1383
+ logBrowserBailout(
1384
+ request,
1385
+ fatalRecoverableError.cause,
1386
+ errorInfo,
1387
+ debugTask,
1388
+ );
1389
return REACT_RECOVERABLE_DIGEST;
1390
}
1391
1413
return errorDigest === '' ? undefined : errorDigest;
1414
}
1415
1416
+function logBrowserBailout(
1417
+ request: Request,
1418
+ error: mixed,
1419
+ errorInfo: ThrownInfo,
1420
+ debugTask: null | ConsoleTask,
1421
+): void {
1422
+ // If this callback errors, we intentionally let that error bubble up to
1423
+ // become a fatal error, matching the behavior of onError.
1424
+ const onBrowserBailout = request.onBrowserBailout;
1425
+ if (__DEV__ && debugTask) {
1426
+ debugTask.run(onBrowserBailout.bind(null, error, errorInfo));
1427
+ } else {
1428
+ onBrowserBailout(error, errorInfo);
1429
+ }
1430
+}
1431
+
1432
function fatalError(
1433
request: Request,
1434
error: mixed,
4899
let errorDigest;
4900
let errorForBoundary;
4901
if (isRecoverableAbort) {
4902
+ logBrowserBailout(request, error, errorInfo, null);
4903
errorDigest = REACT_RECOVERABLE_DIGEST;
4904
errorForBoundary = RecoverableException;
4905
} else {
4947
boundary.status = CLIENT_RENDERED;
4948
// We are aborting a render or resume which should put boundaries
4949
// into an explicitly client rendered state
4911
- const errorDigest = isRecoverableAbort
4912
- ? REACT_RECOVERABLE_DIGEST
4913
- : logRecoverableError(request, error, errorInfo, task.debugTask);
4914
- const errorForBoundary = isRecoverableAbort
4915
- ? RecoverableException
4916
- : error;
4950
+ let errorDigest;
4951
+ let errorForBoundary;
4952
+ if (isRecoverableAbort) {
4953
+ logBrowserBailout(request, error, errorInfo, task.debugTask);
4954
+ errorDigest = REACT_RECOVERABLE_DIGEST;
4955
+ errorForBoundary = RecoverableException;
4956
+ } else {
4957
+ errorDigest = logRecoverableError(
4958
+ request,
4959
+ error,
4960
+ errorInfo,
4961
+ task.debugTask,
4962
+ );
4963
+ errorForBoundary = error;
4964
+ }
4965
encodeErrorForBoundary(
4966
boundary,
4967
errorDigest,