@samitouri / QOS-React / commits / 701d128fd1

Error recovery should have task priority

This uncovered a separate issue where some errors were being unscheduled and rescheduled multiple times before flushing. Turns out we need to check both a fiber and its alternate when determining if it represents a failed unit of work. I didn't notice this before because we were scheduling a new update on *every* boundary at the end of the commit phase, not just the ones that captured an error during that commit. I updated the unit tests to catch this in the future.

Andrew Clark committed Dec 3, 2016 at 12:22 UTC 701d128fd1a7731ee8f40e4611c3433e4c258160
7 files changed +130 -117
src/renderers/shared/fiber/ReactFiberBeginWork.js
+4
@@ -54,6 +54,7 @@ var {
54 var {
55 Placement,
56 ContentReset,
57 + Err,
58 } = require('ReactTypeOfSideEffect');
59 var ReactCurrentOwner = require('ReactCurrentOwner');
60 var ReactFiberClassComponent = require('ReactFiberClassComponent');
@@ -502,6 +503,9 @@ module.exports = function<T, P, I, TI, C>(
503 throw new Error('Invalid type of work');
504 }
505
506 + // Add an error effect so we can handle the error during the commit phase
507 + workInProgress.effectTag |= Err;
508 +
509 if (workInProgress.pendingWorkPriority === NoWork ||
510 workInProgress.pendingWorkPriority > priorityLevel) {
511 return bailoutOnLowPriority(current, workInProgress);
src/renderers/shared/fiber/ReactFiberCommitWork.js
+17 -35
@@ -204,17 +204,15 @@ module.exports = function<T, P, I, TI, C>(
204 }
205 }
206
207 - function commitNestedUnmounts(root : Fiber): boolean {
207 + function commitNestedUnmounts(root : Fiber): void {
208 // While we're inside a removed host node we don't want to call
209 // removeChild on the inner nodes because they're removed by the top
210 // call anyway. We also want to call componentWillUnmount on all
211 // composites before this host node is removed from the tree. Therefore
212 // we do an inner loop while we're still inside the host node.
213 - let noErrors = true;
213 let node : Fiber = root;
214 while (true) {
216 - const noError = commitUnmount(node);
217 - noErrors = noErrors && noError;
215 + commitUnmount(node);
216 if (node.child) {
217 // TODO: Coroutines need to visit the stateNode.
218 node.child.return = node;
@@ -222,32 +220,26 @@ module.exports = function<T, P, I, TI, C>(
220 continue;
221 }
222 if (node === root) {
225 - return noErrors;
223 + return;
224 }
225 while (!node.sibling) {
226 if (!node.return || node.return === root) {
229 - return noErrors;
227 + return;
228 }
229 node = node.return;
230 }
231 node.sibling.return = node.return;
232 node = node.sibling;
233 }
236 - // This is unreachable but without it Flow complains about implicitly-
237 - // returned undefined.
238 - return noErrors; // eslint-disable-line no-unreachable
234 }
235
241 - // Returns true if it completed without any errors
242 - function unmountHostComponents(parent, current): boolean {
236 + function unmountHostComponents(parent, current): void {
237 // We only have the top Fiber that was inserted but we need recurse down its
238 // children to find all the terminal nodes.
245 - let noErrors = true;
239 let node : Fiber = current;
240 while (true) {
241 if (node.tag === HostComponent || node.tag === HostText) {
249 - const noError = commitNestedUnmounts(node);
250 - noErrors = noErrors && noError;
242 + commitNestedUnmounts(node);
243 // After all the children have unmounted, it is now safe to remove the
244 // node from the tree.
245 removeChild(parent, node.stateNode);
@@ -260,8 +252,7 @@ module.exports = function<T, P, I, TI, C>(
252 continue;
253 }
254 } else {
263 - const noError = commitUnmount(node);
264 - noErrors = noErrors && noError;
255 + commitUnmount(node);
256 if (node.child) {
257 // TODO: Coroutines need to visit the stateNode.
258 node.child.return = node;
@@ -270,11 +261,11 @@ module.exports = function<T, P, I, TI, C>(
261 }
262 }
263 if (node === current) {
273 - return noErrors;
264 + return;
265 }
266 while (!node.sibling) {
267 if (!node.return || node.return === current) {
277 - return noErrors;
268 + return;
269 }
270 node = node.return;
271 if (node.tag === HostPortal) {
@@ -286,16 +277,13 @@ module.exports = function<T, P, I, TI, C>(
277 node.sibling.return = node.return;
278 node = node.sibling;
279 }
289 - // This is unreachable but without it Flow complains about implicitly-
290 - // returned undefined.
291 - return noErrors; // eslint-disable-line no-unreachable
280 }
281
294 - function commitDeletion(current : Fiber) : boolean {
282 + function commitDeletion(current : Fiber) : void {
283 // Recursively delete all host nodes from the parent.
284 const parent = getHostParent(current);
285 // Detach refs and call componentWillUnmount() on the whole subtree.
298 - const noErrors = unmountHostComponents(parent, current);
286 + unmountHostComponents(parent, current);
287
288 // Cut off the return pointers to disconnect it from the tree. Ideally, we
289 // should clear the child pointer of the parent alternate to let this
@@ -308,12 +296,9 @@ module.exports = function<T, P, I, TI, C>(
296 current.alternate.child = null;
297 current.alternate.return = null;
298 }
311 - return noErrors;
299 }
300
314 - // Returns true if it completed without any errors
315 - function commitUnmount(current : Fiber) : boolean {
316 - let noErrors = true;
301 + function commitUnmount(current : Fiber) : void {
302 switch (current.tag) {
303 case ClassComponent: {
304 detachRef(current);
@@ -322,27 +307,24 @@ module.exports = function<T, P, I, TI, C>(
307 const error = tryCallComponentWillUnmount(instance);
308 if (error) {
309 captureError(current, error);
325 - noErrors = false;
310 }
311 }
328 - break;
312 + return;
313 }
314 case HostComponent: {
315 detachRef(current);
332 - break;
316 + return;
317 }
318 case CoroutineComponent: {
335 - const noError = commitNestedUnmounts(current.stateNode);
336 - noErrors = noErrors && noError;
337 - break;
319 + commitNestedUnmounts(current.stateNode);
320 + return;
321 }
322 case HostPortal: {
323 // TODO: this is recursive.
324 commitDeletion(current);
342 - break;
325 + return;
326 }
327 }
345 - return noErrors;
328 }
329
330 function commitWork(current : ?Fiber, finishedWork : Fiber) : void {
src/renderers/shared/fiber/ReactFiberScheduler.js
+99 -68
@@ -82,6 +82,8 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
82 // Keeps track of whether we're currently in a work loop. Used to batch
83 // nested updates.
84 let isPerformingWork : boolean = false;
85 + // This lets us know if we should defer a commit until the next frame.
86 + let isPerformingDeferredWork : boolean = false;
87
88 // The next work in progress fiber that we're currently working on.
89 let nextUnitOfWork : ?Fiber = null;
@@ -100,6 +102,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
102 let capturedErrors : Map<Fiber, Error> | null = null;
103 let firstUncaughtError : Error | null = null;
104
105 + let isCommitting : boolean = false;
106 + // Error boundaries that captured an error during the current commit.
107 + let commitPhaseBoundaries : Set<Fiber> | null = null;
108 +
109 let isUnmounting : boolean = false;
110
111 function scheduleAnimationCallback(callback) {
@@ -117,10 +123,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
123 }
124
125 function findNextUnitOfWork() {
120 - if (pendingCommit) {
121 - return pendingCommit;
122 - }
123 -
126 // Clear out roots with no more work on them, or if they have uncaught errors
127 while (nextScheduledRoot && nextScheduledRoot.current.pendingWorkPriority === NoWork) {
128 // Unschedule this root.
@@ -140,6 +142,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
142 // If there's no work on it, it will get unscheduled too.
143 nextScheduledRoot = next;
144 }
145 + if (pendingCommit) {
146 + nextPriorityLevel = pendingCommit.pendingWorkPriority;
147 + return null;
148 + }
149 let root = nextScheduledRoot;
150 let highestPriorityRoot = null;
151 let highestPriorityLevel = NoWork;
@@ -167,11 +173,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
173
174 // Returns true if the commit phase finishes without any errors.
175 function commitAllWork(finishedWork : Fiber): boolean {
170 - let noErrors = true;
176 + // We keep track of this so that captureError can collect any boundaries
177 + // that capture an error during the commit phase. The reason these aren't
178 + // local to this function is because errors that occur during cWU are
179 + // captured elsewhere, to prevent the unmount from being interrupted.
180 + isCommitting = true;
181
182 pendingCommit = null;
183 const root : FiberRoot = (finishedWork.stateNode : any);
174 - resetWorkPriority(finishedWork);
184 if (root.current === finishedWork) {
185 throw new Error(
186 'Cannot commit the same tree as before. This is probably a bug ' +
@@ -180,6 +189,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
189 }
190 root.current = finishedWork;
191
192 + // Updates that occur during the commit phase should have Task priority
193 + const previousPriorityContext = priorityContext;
194 + priorityContext = TaskPriority;
195 +
196 prepareForCommit();
197
198 // Commit all the side-effects within a tree.
@@ -229,8 +242,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
242 }
243 case Deletion: {
244 isUnmounting = true;
232 - const noError = commitDeletion(effectfulFiber);
233 - noErrors = noErrors && noError;
245 + commitDeletion(effectfulFiber);
246 isUnmounting = false;
247 break;
248 }
@@ -246,7 +258,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
258 commitWork(current, finishedWork);
259 }
260 } catch (error) {
249 - noErrors = false;
261 captureError(effectfulFiber || null, error);
262 if (effectfulFiber) {
263 effectfulFiber = effectfulFiber.nextEffect;
@@ -264,8 +275,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
275 // happens as a separate pass so that all effects in the entire tree have
276 // already been invoked.
277 effectfulFiber = finishedWork.firstEffect;
267 - const previousPriorityContext = priorityContext;
268 - priorityContext = TaskPriority;
278 pass2: while (true) {
279 let didHandleRoot = false;
280 try {
@@ -302,7 +311,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
311 }
312 }
313 } catch (error) {
305 - noErrors = false;
314 captureError(effectfulFiber || null, error);
315 if (effectfulFiber) {
316 const next = effectfulFiber.nextEffect;
@@ -315,28 +323,26 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
323 break;
324 }
325
318 - if (capturedErrors) {
319 - if (capturedErrors.size) {
320 - capturedErrors.forEach((error, boundary) => {
321 - scheduleUpdate(boundary);
322 - });
323 - } else {
324 - capturedErrors = null;
325 - }
326 + isCommitting = false;
327 +
328 + // If we caught any errors during this commit, schedule their boundaries
329 + // to update.
330 + let noErrors;
331 + if (commitPhaseBoundaries) {
332 + noErrors = false;
333 + commitPhaseBoundaries.forEach(scheduleUpdate);
334 + commitPhaseBoundaries = null;
335 + } else {
336 + noErrors = true;
337 }
338
339 + priorityContext = previousPriorityContext;
340 nextUnitOfWork = null;
341
342 return noErrors;
343 }
344
345 function resetWorkPriority(workInProgress : Fiber) {
334 - if (workInProgress.pendingProps) {
335 - // Don't reset the priority if the workInProgress has pending props. This
336 - // could happen if a completed root is updated before it is able to commit.
337 - return;
338 - }
339 -
346 let newPriority = NoWork;
347 // progressedChild is going to be the child set with the highest priority.
348 // Either it is the same as child, or it just bailed out because it choose
@@ -370,11 +376,8 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
376
377 const returnFiber = workInProgress.return;
378 const siblingFiber = workInProgress.sibling;
373 - const isRoot = !returnFiber && !siblingFiber;
379
375 - if (!isRoot) {
376 - resetWorkPriority(workInProgress);
377 - }
380 + resetWorkPriority(workInProgress);
381
382 if (next) {
383 // If completing this work spawned new work, do that next. We'll come
@@ -382,12 +385,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
385 return next;
386 }
387
385 - if (isRoot) {
386 - // If we've reached the root, commit during the next unit of work.
387 - pendingCommit = workInProgress;
388 - return workInProgress;
389 - }
390 -
388 if (returnFiber) {
389 // Append all the effects of the subtree and this fiber onto the effect
390 // list of the parent. The completion order of the children affects the
@@ -426,7 +423,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
423 workInProgress = returnFiber;
424 continue;
425 } else {
429 - throw new Error('Should have already handled root.');
426 + // If we've reached the root, commit during the next unit of work.
427 + pendingCommit = workInProgress;
428 + return null;
429 }
430 }
431 }
@@ -476,7 +475,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
475 }
476 // See if beginning this work spawns more work.
477 let next = beginFailedWork(current, workInProgress, nextPriorityLevel);
479 - workInProgress.effectTag |= Err;
478
479 if (__DEV__ && ReactFiberInstrumentation.debugTool) {
480 ReactFiberInstrumentation.debugTool.onDidBeginWork(workInProgress);
@@ -517,22 +515,25 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
515 }
516
517 // If there are errors, use a forked version of performUnitOfWork
520 - // TODO: Need to make commitAllWork throw on error before doing this
518 if (capturedErrors && capturedErrors.size) {
519 while (true) {
520 if (!nextUnitOfWork && !pendingCommit) {
521 nextUnitOfWork = findNextUnitOfWork();
522 }
526 - // If we're currently flushing Task work, but the pending commit does
527 - // not have Task priority, don't commit it — we're at the end of a
528 - // deferred work batch and the commit should happen in the next batch.
523 +
524 + // If we're performing Task work and we're inside a deferred work batch,
525 + // then we shouldn't commit any work yet. Defer it to the next frame.
526 + // TODO: Is there a better way check if the pending commit should be
527 + // deferred? It's not as simple as retaining work priority because
528 + // the root may receive a lower priority update before the completed
529 + // work is flushed, causing the new update to be up-prioritized.
530 if (
531 pendingCommit &&
531 - !(priorityLevel === TaskPriority && pendingCommit.pendingWorkPriority !== TaskPriority)
532 + !(priorityLevel === TaskPriority && isPerformingDeferredWork)
533 ) {
534 commitAllWork(pendingCommit);
535 } else if (nextUnitOfWork && nextPriorityLevel !== NoWork && nextPriorityLevel <= priorityLevel) {
535 - if (capturedErrors && capturedErrors.has(nextUnitOfWork)) {
536 + if (isFailedWork(nextUnitOfWork)) {
537 nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
538 } else {
539 nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
@@ -548,12 +549,16 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
549 if (!nextUnitOfWork && !pendingCommit) {
550 nextUnitOfWork = findNextUnitOfWork();
551 }
551 - // If we're currently flushing Task work, but the pending commit does
552 - // not have Task priority, don't commit it — we're at the end of a
553 - // deferred work batch and the commit should happen in the next batch.
552 +
553 + // If we're performing Task work and we're inside a deferred work batch,
554 + // then we shouldn't commit any work yet. Defer it to the next frame.
555 + // TODO: Is there a better way check if the pending commit should be
556 + // deferred? It's not as simple as retaining work priority because
557 + // the root may receive a lower priority update before the completed
558 + // work is flushed, causing the new update to be up-prioritized.
559 if (
560 pendingCommit &&
556 - !(priorityLevel === TaskPriority && pendingCommit.pendingWorkPriority !== TaskPriority)
561 + !(priorityLevel === TaskPriority && isPerformingDeferredWork)
562 ) {
563 const noError = commitAllWork(pendingCommit);
564 if (!noError) {
@@ -578,7 +583,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
583 }
584
585 // If there are errors, use a forked version of performUnitOfWork
581 - // TODO: Need to make commitAllWork throw on error before doing this
586 if (capturedErrors && capturedErrors.size) {
587 while (true) {
588 if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
@@ -588,7 +592,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
592 if (pendingCommit) {
593 commitAllWork(pendingCommit);
594 } else if (nextUnitOfWork) {
591 - if (capturedErrors && capturedErrors.has(nextUnitOfWork)) {
595 + if (isFailedWork(nextUnitOfWork)) {
596 nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
597 } else {
598 nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
@@ -633,6 +637,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
637 throw new Error('performWork was called recursively.');
638 }
639 isPerformingWork = true;
640 + isPerformingDeferredWork = priorityLevel >= HighPriority;
641
642 // Perform work until either there's no more work at this priority level, or
643 // (in the case of deferred work) we've run out of time.
@@ -694,13 +699,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
699 if (maybeBoundary) {
700 const boundary = maybeBoundary;
701
697 - // Schedule an Task update on the boundary
698 - const previousPriorityContext = priorityContext;
699 - priorityContext = TaskPriority;
700 - scheduleUpdate(boundary);
701 - priorityContext = previousPriorityContext;
702 -
703 - // Complete the boundary as if rendered null.
702 + // Complete the boundary as if it rendered null.
703 beginFailedWork(boundary.alternate, boundary, priorityLevel);
704
705 // The next unit of work is now the boundary that captured the error.
@@ -724,6 +723,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
723
724 // We're done performing work. Time to clean up.
725 isPerformingWork = false;
726 + isPerformingDeferredWork = false;
727 if (capturedErrors && !capturedErrors.size) {
728 capturedErrors = null;
729 }
@@ -736,6 +736,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
736 }
737 }
738
739 + // Returns the boundary that captured the error, or null if the error is ignored
740 function captureError(failedWork : Fiber | null, error : Error) : Fiber | null {
741 // It is no longer valid because we exited the user code.
742 ReactCurrentOwner.current = null;
@@ -784,11 +785,25 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
785 if (!capturedErrors) {
786 capturedErrors = new Map();
787 }
788 +
789 // Ensure that neither this boundary nor its alternate has captured an
790 // error already.
789 - if (!capturedErrors.has(boundary) &&
790 - !(boundary.alternate && capturedErrors.has(boundary.alternate))) {
791 + if (!isFailedWork(boundary)) {
792 capturedErrors.set(boundary, error);
793 + // If we're in the commit phase, defer scheduling an update on the
794 + // boundary until after the commit is complete
795 + if (isCommitting) {
796 + if (!commitPhaseBoundaries) {
797 + commitPhaseBoundaries = new Set();
798 + }
799 + commitPhaseBoundaries.add(boundary);
800 + } else {
801 + // Otherwise, schedule an update now. Error recovery has Task priority.
802 + const previousPriorityContext = priorityContext;
803 + priorityContext = TaskPriority;
804 + scheduleUpdate(boundary);
805 + priorityContext = previousPriorityContext;
806 + }
807 }
808 return boundary;
809 } else if (!firstUncaughtError) {
@@ -798,27 +813,37 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
813 return null;
814 }
815
816 + function isFailedWork(fiber : Fiber) : boolean {
817 + return Boolean(
818 + capturedErrors &&
819 + (capturedErrors.has(fiber) || (fiber.alternate && capturedErrors.has(fiber.alternate)))
820 + );
821 + }
822 +
823 function commitErrorHandling(effectfulFiber : Fiber) {
824 let error;
825 if (capturedErrors) {
804 - // Get the error associated with the fiber being commited.
826 error = capturedErrors.get(effectfulFiber);
827 capturedErrors.delete(effectfulFiber);
828 + if (!error) {
829 + if (effectfulFiber.alternate) {
830 + effectfulFiber = effectfulFiber.alternate;
831 + error = capturedErrors.get(effectfulFiber);
832 + capturedErrors.delete(effectfulFiber);
833 + }
834 + }
835 }
836 +
837 if (!error) {
809 - throw new Error('No matching captured error.');
838 + throw new Error('No error for given unit of work.');
839 }
840
841 switch (effectfulFiber.tag) {
842 case ClassComponent:
843 const instance = effectfulFiber.stateNode;
815 - try {
816 - // Allow the boundary to handle the error, usually by scheduling
817 - // an update to itself
818 - instance.unstable_handleError(error);
819 - } catch (e) {
820 - captureError(effectfulFiber, e);
821 - }
844 + // Allow the boundary to handle the error, usually by scheduling
845 + // an update to itself
846 + instance.unstable_handleError(error);
847 return;
848 case HostRoot:
849 if (!firstUncaughtError) {
@@ -850,6 +875,12 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
875 priorityLevel <= root.current.pendingWorkPriority) {
876 root.current.pendingWorkPriority = priorityLevel;
877 }
878 + if (root.current.alternate) {
879 + if (root.current.alternate.pendingWorkPriority === NoWork ||
880 + priorityLevel <= root.current.alternate.pendingWorkPriority) {
881 + root.current.alternate.pendingWorkPriority = priorityLevel;
882 + }
883 + }
884
885 if (!root.isScheduled) {
886 root.isScheduled = true;
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
+1 -5
@@ -88,7 +88,7 @@ describe('ReactIncrementalErrorHandling', () => {
88 expect(ReactNoop.getChildren()).toEqual([]);
89
90 ops.length = 0;
91 - ReactNoop.flushDeferredPri(30 + 5 + 5 + 5 + 5);
91 + ReactNoop.flushDeferredPri(25 + 10);
92 expect(ops).toEqual([
93 'BrokenRender',
94 'ErrorBoundary unstable_handleError',
@@ -132,10 +132,6 @@ describe('ReactIncrementalErrorHandling', () => {
132 expect(ops).toEqual([
133 'ErrorBoundary render success',
134 'BrokenRender',
135 - ]);
136 - ops = [];
137 - ReactNoop.flushDeferredPri(25 + 5 + 5 + 5);
138 - expect(ops).toEqual([
135 'ErrorBoundary unstable_handleError',
136 'ErrorBoundary render error',
137 ]);
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
+2 -2
@@ -483,7 +483,7 @@ describe('ReactIncrementalScheduling', () => {
483
484 ReactNoop.render(<Foo />);
485
486 - ReactNoop.flushDeferredPri(20 + 5 + 5);
486 + ReactNoop.flushDeferredPri(5 + 20 + 20);
487 expect(ops).toEqual([
488 'render: 0',
489 'componentDidMount (before setState): 0',
@@ -496,7 +496,7 @@ describe('ReactIncrementalScheduling', () => {
496
497 ops = [];
498 instance.setState({ tick: 2 });
499 - ReactNoop.flushDeferredPri(20 + 5 + 5);
499 + ReactNoop.flushDeferredPri(5 + 20 + 20);
500
501 expect(ops).toEqual([
502 'render: 2',
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
+1 -1
@@ -491,7 +491,7 @@ describe('ReactIncrementalSideEffects', () => {
491 // To confirm, perform one more unit of work. The tree should now be flushed.
492 // (ReactNoop decrements the time remaining by 5 *before* returning it from
493 // the deadline, so to perform n units of work, you need to give it 5n + 5.
494 - // TODO: This is confusing. Decerement it after.)
494 + // TODO: This is confusing. Decrement it after.)
495 ReactNoop.flushDeferredPri(10);
496 expect(ReactNoop.getChildren()).toEqual([span(1)]);
497
src/renderers/shared/shared/__tests__/ReactErrorBoundaries-test.js
+6 -6
@@ -808,8 +808,8 @@ describe('ReactErrorBoundaries', () => {
808 ...(ReactDOMFeatureFlags.useFiber ? [
809 // In Fiber, noop error boundaries render null
810 'RethrowErrorBoundary componentDidMount',
811 - 'ErrorBoundary componentDidMount',
811 'RethrowErrorBoundary unstable_handleError [!]',
812 + 'ErrorBoundary componentDidMount',
813 // The error got rethrown here.
814 // This time, the error propagates to the higher boundary
815 'RethrowErrorBoundary componentWillUnmount',
@@ -904,9 +904,9 @@ describe('ReactErrorBoundaries', () => {
904 ...(ReactDOMFeatureFlags.useFiber ? [
905 // Finish mounting with null children
906 'BrokenRenderErrorBoundary componentDidMount',
907 - 'ErrorBoundary componentDidMount',
907 // Attempt to handle the error
908 'BrokenRenderErrorBoundary unstable_handleError',
909 + 'ErrorBoundary componentDidMount',
910 'BrokenRenderErrorBoundary render error [!]',
911 // Boundary fails with new error, propagate to next boundary
912 'BrokenRenderErrorBoundary componentWillUnmount',
@@ -2044,15 +2044,15 @@ describe('ReactErrorBoundaries', () => {
2044 'OuterErrorBoundary componentDidUpdate',
2045 // After the commit phase, attempt to recover from any errors that
2046 // were captured
2047 - 'InnerUnmountBoundary unstable_handleError',
2048 - 'InnerUnmountBoundary componentWillUpdate',
2049 - 'InnerUnmountBoundary render error',
2047 'BrokenComponentDidUpdate componentWillUnmount',
2048 'BrokenComponentDidUpdate componentWillUnmount',
2052 - 'InnerUnmountBoundary componentDidUpdate',
2049 + 'InnerUnmountBoundary unstable_handleError',
2050 'InnerUpdateBoundary unstable_handleError',
2051 + 'InnerUnmountBoundary componentWillUpdate',
2052 + 'InnerUnmountBoundary render error',
2053 'InnerUpdateBoundary componentWillUpdate',
2054 'InnerUpdateBoundary render error',
2055 + 'InnerUnmountBoundary componentDidUpdate',
2056 'InnerUpdateBoundary componentDidUpdate',
2057 ]);
2058