Use invokeGuardedCallback in place of Fiber try-catch blocks
Only in DEV. In production, continue using a local try-catch.
Andrew Clark committed
Feb 22, 2017 at 14:29 UTC
7e348acab777a29ad86564c4d14dddc385cea8e1
2 files changed
+68
-31
src/renderers/shared/fiber/ReactFiberCommitWork.js
+25
-11
@@ -26,6 +26,7 @@ var {
26
} = ReactTypeOfWork;
27
var { commitCallbacks } = require('ReactFiberUpdateQueue');
28
var { onCommitUnmount } = require('ReactFiberDevToolsHook');
29
+var { invokeGuardedCallback } = require('ReactErrorUtils');
30
31
var {
32
Placement,
@@ -54,22 +55,35 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
55
56
// Capture errors so they don't interrupt unmounting.
57
function safelyCallComponentWillUnmount(current, instance) {
57
- try {
58
- instance.componentWillUnmount();
59
- } catch (error) {
60
- captureError(current, error);
58
+ if (__DEV__) {
59
+ const unmountError = invokeGuardedCallback(null, instance.componentWillUnmount, instance);
60
+ if (unmountError) {
61
+ captureError(current, unmountError);
62
+ }
63
+ } else {
64
+ try {
65
+ instance.componentWillUnmount();
66
+ } catch (unmountError) {
67
+ captureError(current, unmountError);
68
+ }
69
}
70
}
71
64
- // Capture errors so they don't interrupt unmounting.
72
function safelyDetachRef(current : Fiber) {
66
- try {
67
- const ref = current.ref;
68
- if (ref !== null) {
69
- ref(null);
73
+ const ref = current.ref;
74
+ if (ref !== null) {
75
+ if (__DEV__) {
76
+ const refError = invokeGuardedCallback(null, ref, null, null);
77
+ if (refError !== null) {
78
+ captureError(current, refError);
79
+ }
80
+ } else {
81
+ try {
82
+ ref(null);
83
+ } catch (refError) {
84
+ captureError(current, refError);
85
+ }
86
}
71
- } catch (error) {
72
- captureError(current, error);
87
}
88
}
89
src/renderers/shared/fiber/ReactFiberScheduler.js
+43
-20
@@ -35,6 +35,7 @@ var {
35
getStackAddendumByWorkInProgressFiber,
36
} = require('ReactComponentTreeHook');
37
var { logCapturedError } = require('ReactFiberErrorLogger');
38
+var { invokeGuardedCallback } = require('ReactErrorUtils');
39
40
var ReactFiberBeginWork = require('ReactFiberBeginWork');
41
var ReactFiberCompleteWork = require('ReactFiberCompleteWork');
@@ -164,6 +165,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
165
// Keeps track of whether we're currently in a work loop.
166
let isPerformingWork : boolean = false;
167
168
+ // Keeps track of whether the current deadline has expired.
169
+ let deadlineHasExpired : boolean = false;
170
+
171
// Keeps track of whether we should should batch sync updates.
172
let isBatchingUpdates : boolean = false;
173
@@ -414,9 +418,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
418
// ref unmounts.
419
nextEffect = firstEffect;
420
while (nextEffect !== null) {
417
- try {
418
- commitAllHostEffects(finishedWork);
419
- } catch (error) {
421
+ let error = null;
422
+ if (__DEV__) {
423
+ error = invokeGuardedCallback(null, commitAllHostEffects, null, finishedWork);
424
+ } else {
425
+ try {
426
+ commitAllHostEffects(finishedWork);
427
+ } catch (e) {
428
+ error = e;
429
+ }
430
+ }
431
+ if (error !== null) {
432
invariant(
433
nextEffect !== null,
434
'Should have next effect. This error is likely caused by a bug ' +
@@ -444,9 +456,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
456
// This pass also triggers any renderer-specific initial effects.
457
nextEffect = firstEffect;
458
while (nextEffect !== null) {
447
- try {
448
- commitAllLifeCycles(finishedWork, nextEffect);
449
- } catch (error) {
459
+ let error = null;
460
+ if (__DEV__) {
461
+ error = invokeGuardedCallback(null, commitAllLifeCycles, null, finishedWork);
462
+ } else {
463
+ try {
464
+ commitAllLifeCycles(finishedWork);
465
+ } catch (e) {
466
+ error = e;
467
+ }
468
+ }
469
+ if (error !== null) {
470
invariant(
471
nextEffect !== null,
472
'Should have next effect. This error is likely caused by a bug ' +
@@ -675,7 +695,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
695
}
696
}
697
678
- function workLoop(priorityLevel, deadline : Deadline | null, deadlineHasExpired : boolean) : boolean {
698
+ function workLoop(priorityLevel, deadline : Deadline | null) {
699
// Clear any errors.
700
clearErrors();
701
@@ -743,8 +763,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
763
if (hostRootTimeMarker) {
764
console.timeEnd(hostRootTimeMarker);
765
}
746
-
747
- return deadlineHasExpired;
766
}
767
768
function performWork(priorityLevel : PriorityLevel, deadline : Deadline | null) {
@@ -755,7 +773,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
773
);
774
isPerformingWork = true;
775
const isPerformingDeferredWork = Boolean(deadline);
758
- let deadlineHasExpired = false;
776
777
// This outer loop exists so that we can restart the work loop after
778
// catching an error. It also lets us flush Task work at the end of a
@@ -776,18 +793,25 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
793
// Nothing in performWork should be allowed to throw. All unsafe
794
// operations must happen within workLoop, which is extracted to a
795
// separate function so that it can be optimized by the JS engine.
779
- try {
780
- priorityContextBeforeReconciliation = priorityContext;
781
- priorityContext = nextPriorityLevel;
782
- deadlineHasExpired = workLoop(priorityLevel, deadline, deadlineHasExpired);
783
- } catch (error) {
796
+ priorityContextBeforeReconciliation = priorityContext;
797
+ let error = null;
798
+ if (__DEV__) {
799
+ error = invokeGuardedCallback(null, workLoop, null, priorityLevel, deadline);
800
+ } else {
801
+ try {
802
+ workLoop(priorityLevel, deadline);
803
+ } catch (e) {
804
+ error = e;
805
+ }
806
+ }
807
+ // Reset the priority context to its value before reconcilation.
808
+ priorityContext = priorityContextBeforeReconciliation;
809
+
810
+ if (error !== null) {
811
// We caught an error during either the begin or complete phases.
812
const failedWork = nextUnitOfWork;
813
814
if (failedWork !== null) {
788
- // Reset the priority context to its value before reconciliation.
789
- priorityContext = priorityContextBeforeReconciliation;
790
-
815
// "Capture" the error by finding the nearest boundary. If there is no
816
// error boundary, the nearest host container acts as one. If
817
// captureError returns null, the error was intentionally ignored.
@@ -818,8 +842,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
842
// inside resetAfterCommit.
843
fatalError = error;
844
}
821
- } finally {
822
- priorityContext = priorityContextBeforeReconciliation;
845
}
846
847
// Stop performing work
@@ -862,6 +884,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
884
885
// We're done performing work. Time to clean up.
886
isPerformingWork = false;
887
+ deadlineHasExpired = false;
888
fatalError = null;
889
firstUncaughtError = null;
890
capturedErrors = null;