Remove Animation priority
There's no advantage to scheduling updates with animation priority versus scheduling sync work inside requestAnimationCallback. So we can remove all the animation-specific code. Now there's only one type of callback.
Andrew Clark committed
Jun 15, 2017 at 15:35 UTC
812244b57abda11ba3cd101180dec94970415e84
18 files changed
+288
-579
package.json
+1
-1
@@ -67,7 +67,7 @@
67
"jest": "^19.0.1",
68
"jest-config": "^19.0.1",
69
"jest-jasmine2": "^19.0.1",
70
- "jest-matchers": "^20.0.3",
70
+ "jest-matchers": "^19.0.1",
71
"jest-runtime": "^19.0.1",
72
"loose-envify": "^1.1.0",
73
"merge-stream": "^1.0.0",
scripts/fiber/tests-passing.txt
+2
-6
@@ -1791,12 +1791,10 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
1791
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
1792
* catches render error in a boundary during full deferred mounting
1793
* catches render error in a boundary during partial deferred mounting
1794
-* catches render error in a boundary during animation mounting
1794
* catches render error in a boundary during synchronous mounting
1795
* catches render error in a boundary during batched mounting
1796
* propagates an error from a noop error boundary during full deferred mounting
1797
* propagates an error from a noop error boundary during partial deferred mounting
1799
-* propagates an error from a noop error boundary during animation mounting
1798
* propagates an error from a noop error boundary during synchronous mounting
1799
* propagates an error from a noop error boundary during batched mounting
1800
* applies batched updates regardless despite errors in scheduling
@@ -1842,18 +1840,16 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalReflection-test.js
1840
1841
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
1842
* schedules and flushes deferred work
1845
-* schedules and flushes animation work
1843
* searches for work on other roots once the current root completes
1847
-* schedules an animation callback when there`s leftover animation work
1844
* schedules top-level updates in order of priority
1845
* schedules top-level updates with same priority in order of insertion
1846
* works on deferred roots in the order they were scheduled
1847
* schedules sync updates when inside componentDidMount/Update
1852
-* can opt-in to deferred/animation scheduling inside componentDidMount/Update
1848
+* can opt-in to async scheduling inside componentDidMount/Update
1849
* performs Task work even after time runs out
1854
-* does not perform animation work after time runs out
1850
* can opt-out of batching using unbatchedUpdates
1851
* nested updates are always deferred, even inside unbatchedUpdates
1852
+* updates do not schedule a new callback if already inside a callback
1853
1854
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
1855
* can update child nodes of a host instance
scripts/rollup/results.json
+24
-24
@@ -25,28 +25,28 @@
25
"gzip": 7690
26
},
27
"react-dom.development.js (UMD_DEV)": {
28
- "size": 609702,
29
- "gzip": 139802
28
+ "size": 608162,
29
+ "gzip": 139430
30
},
31
"react-dom.production.min.js (UMD_PROD)": {
32
- "size": 126123,
33
- "gzip": 39666
32
+ "size": 125771,
33
+ "gzip": 39564
34
},
35
"react-dom.development.js (NODE_DEV)": {
36
- "size": 568320,
37
- "gzip": 130089
36
+ "size": 566845,
37
+ "gzip": 129774
38
},
39
"react-dom.production.min.js (NODE_PROD)": {
40
- "size": 122488,
41
- "gzip": 38393
40
+ "size": 122136,
41
+ "gzip": 38292
42
},
43
"ReactDOMFiber-dev.js (FB_DEV)": {
44
- "size": 568338,
45
- "gzip": 130385
44
+ "size": 566594,
45
+ "gzip": 129950
46
},
47
"ReactDOMFiber-prod.js (FB_PROD)": {
48
- "size": 428140,
49
- "gzip": 96977
48
+ "size": 426211,
49
+ "gzip": 96543
50
},
51
"react-dom-test-utils.development.js (NODE_DEV)": {
52
"size": 52921,
@@ -65,28 +65,28 @@
65
"gzip": 81957
66
},
67
"react-dom-server.development.js (UMD_DEV)": {
68
- "size": 305758,
69
- "gzip": 76967
68
+ "size": 306445,
69
+ "gzip": 77153
70
},
71
"react-dom-server.production.min.js (UMD_PROD)": {
72
- "size": 65877,
73
- "gzip": 22507
72
+ "size": 65952,
73
+ "gzip": 22521
74
},
75
"react-dom-server.development.js (NODE_DEV)": {
76
- "size": 264541,
77
- "gzip": 67428
76
+ "size": 265293,
77
+ "gzip": 67673
78
},
79
"react-dom-server.production.min.js (NODE_PROD)": {
80
- "size": 62227,
81
- "gzip": 21217
80
+ "size": 62302,
81
+ "gzip": 21232
82
},
83
"ReactDOMServerStream-dev.js (FB_DEV)": {
84
- "size": 264063,
85
- "gzip": 67393
84
+ "size": 264750,
85
+ "gzip": 67600
86
},
87
"ReactDOMServerStream-prod.js (FB_PROD)": {
88
- "size": 197791,
89
- "gzip": 51009
88
+ "size": 198041,
89
+ "gzip": 51047
90
},
91
"react-art.development.js (UMD_DEV)": {
92
"size": 359725,
src/renderers/art/ReactARTFiberEntry.js
-2
@@ -526,8 +526,6 @@ const ARTRenderer = ReactFiberReconciler({
526
return emptyObject;
527
},
528
529
- scheduleAnimationCallback: ReactDOMFrameScheduling.rAF,
530
-
529
scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
530
531
shouldSetTextContent(type, props) {
src/renderers/dom/fiber/ReactDOMFiberEntry.js
-2
@@ -445,8 +445,6 @@ var DOMRenderer = ReactFiberReconciler({
445
return textInstance.nodeValue !== text;
446
},
447
448
- scheduleAnimationCallback: ReactDOMFrameScheduling.rAF,
449
-
448
scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
449
450
useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling,
src/renderers/native/ReactNativeFiberRenderer.js
-2
@@ -363,8 +363,6 @@ const NativeRenderer = ReactFiberReconciler({
363
return false;
364
},
365
366
- scheduleAnimationCallback: global.requestAnimationFrame,
367
-
366
scheduleDeferredCallback: global.requestIdleCallback,
367
368
shouldSetTextContent(type: string, props: Props): boolean {
src/renderers/noop/ReactNoopEntry.js
+20
-52
@@ -25,15 +25,14 @@ import type {UpdateQueue} from 'ReactFiberUpdateQueue';
25
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
26
var ReactFiberReconciler = require('ReactFiberReconciler');
27
var ReactInstanceMap = require('ReactInstanceMap');
28
-var {AnimationPriority} = require('ReactPriorityLevel');
28
+var {TaskPriority} = require('ReactPriorityLevel');
29
var emptyObject = require('fbjs/lib/emptyObject');
30
31
var expect = require('jest-matchers');
32
33
const UPDATE_SIGNAL = {};
34
35
-var scheduledAnimationCallback = null;
36
-var scheduledDeferredCallback = null;
35
+var scheduledCallback = null;
36
37
type Container = {rootID: string, children: Array<Instance | TextInstance>};
38
type Props = {prop: any, hidden?: boolean};
@@ -192,24 +191,14 @@ var NoopRenderer = ReactFiberReconciler({
191
removeChild: removeChild,
192
removeChildFromContainer: removeChild,
193
195
- scheduleAnimationCallback(callback) {
196
- if (scheduledAnimationCallback) {
197
- throw new Error(
198
- 'Scheduling an animation callback twice is excessive. ' +
199
- 'Instead, keep track of whether the callback has already been scheduled.',
200
- );
201
- }
202
- scheduledAnimationCallback = callback;
203
- },
204
-
194
scheduleDeferredCallback(callback) {
206
- if (scheduledDeferredCallback) {
195
+ if (scheduledCallback) {
196
throw new Error(
208
- 'Scheduling deferred callback twice is excessive. ' +
209
- 'Instead, keep track of whether the callback has already been scheduled.',
197
+ 'Scheduling a callback twice is excessive. Instead, keep track of ' +
198
+ 'whether the callback has already been scheduled.',
199
);
200
}
212
- scheduledDeferredCallback = callback;
201
+ scheduledCallback = callback;
202
},
203
204
prepareForCommit(): void {},
@@ -223,13 +212,11 @@ var DEFAULT_ROOT_ID = '<default>';
212
213
let yieldedValues = null;
214
226
-function* flushUnitsOfWork(
227
- n: number = Infinity,
228
-): Generator<Array<mixed>, void, void> {
215
+function* flushUnitsOfWork(n: number): Generator<Array<mixed>, void, void> {
216
var didStop = false;
230
- while (!didStop && scheduledDeferredCallback !== null) {
231
- var cb = scheduledDeferredCallback;
232
- scheduledDeferredCallback = null;
217
+ while (!didStop && scheduledCallback !== null) {
218
+ var cb = scheduledCallback;
219
+ scheduledCallback = null;
220
yieldedValues = null;
221
var unitsRemaining = n;
222
cb({
@@ -308,16 +295,6 @@ var ReactNoop = {
295
return inst ? NoopRenderer.findHostInstance(inst) : null;
296
},
297
311
- // TODO: Remove this method
312
- flushAnimationPri() {
313
- var cb = scheduledAnimationCallback;
314
- if (cb === null) {
315
- return;
316
- }
317
- scheduledAnimationCallback = null;
318
- cb();
319
- },
320
-
298
flushDeferredPri(timeout: number = Infinity): Array<mixed> {
299
// The legacy version of this function decremented the timeout before
300
// returning the new time.
@@ -328,23 +305,17 @@ var ReactNoop = {
305
for (const value of flushUnitsOfWork(n)) {
306
values.push(...value);
307
}
331
- // Don't flush animation priority in this legacy function. Some tests may
332
- // still rely on this behavior.
308
return values;
309
},
310
311
flush(): Array<mixed> {
337
- ReactNoop.flushAnimationPri();
338
- return ReactNoop.flushDeferredPri();
312
+ return ReactNoop.flushUnitsOfWork(Infinity);
313
},
314
341
- *flushAndYield(
315
+ flushAndYield(
316
unitsOfWork: number = Infinity,
317
): Generator<Array<mixed>, void, void> {
344
- for (const value of flushUnitsOfWork(unitsOfWork)) {
345
- yield value;
346
- }
347
- ReactNoop.flushAnimationPri();
318
+ return flushUnitsOfWork(unitsOfWork);
319
},
320
321
flushUnitsOfWork(n: number): Array<mixed> {
@@ -352,24 +323,19 @@ var ReactNoop = {
323
for (const value of flushUnitsOfWork(n)) {
324
values.push(...value);
325
}
355
- // TODO: We should always flush animation priority after flushing normal/low
356
- // priority. Move this to flushUnitsOfWork generator once tests
357
- // are converted.
358
- ReactNoop.flushAnimationPri();
326
return values;
327
},
328
329
flushThrough(expected: Array<mixed>): void {
330
let actual = [];
331
if (expected.length !== 0) {
365
- for (const value of flushUnitsOfWork()) {
332
+ for (const value of flushUnitsOfWork(Infinity)) {
333
actual.push(...value);
334
if (actual.length >= expected.length) {
335
break;
336
}
337
}
338
}
372
- ReactNoop.flushAnimationPri();
339
expect(actual).toEqual(expected);
340
},
341
@@ -381,16 +347,18 @@ var ReactNoop = {
347
}
348
},
349
384
- hasScheduledDeferredCallback() {
385
- return !!scheduledDeferredCallback;
350
+ hasScheduledCallback() {
351
+ return !!scheduledCallback;
352
},
353
388
- performAnimationWork(fn: Function) {
389
- NoopRenderer.performWithPriority(AnimationPriority, fn);
354
+ taskUpdates(fn: Function) {
355
+ NoopRenderer.performWithPriority(TaskPriority, fn);
356
},
357
358
batchedUpdates: NoopRenderer.batchedUpdates,
359
360
+ deferredUpdates: NoopRenderer.deferredUpdates,
361
+
362
unbatchedUpdates: NoopRenderer.unbatchedUpdates,
363
364
syncUpdates: NoopRenderer.syncUpdates,
src/renderers/shared/fiber/ReactFiberReconciler.js
-1
@@ -114,7 +114,6 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
114
removeChild(parentInstance: I, child: I | TI): void,
115
removeChildFromContainer(container: C, child: I | TI): void,
116
117
- scheduleAnimationCallback(callback: () => void): number | void,
117
scheduleDeferredCallback(
118
callback: (deadline: Deadline) => void,
119
): number | void,
src/renderers/shared/fiber/ReactFiberScheduler.js
+88
-134
@@ -55,7 +55,6 @@ var {
55
NoWork,
56
SynchronousPriority,
57
TaskPriority,
58
- AnimationPriority,
58
HighPriority,
59
LowPriority,
60
OffscreenPriority,
@@ -171,8 +170,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
170
commitDetachRef,
171
} = ReactFiberCommitWork(config, captureError);
172
const {
174
- scheduleAnimationCallback: hostScheduleAnimationCallback,
175
- scheduleDeferredCallback: hostScheduleDeferredCallback,
173
+ scheduleDeferredCallback,
174
useSyncScheduling,
175
prepareForCommit,
176
resetAfterCommit,
@@ -211,8 +209,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
209
let lastScheduledRoot: FiberRoot | null = null;
210
211
// Keep track of which host environment callbacks are scheduled.
214
- let isAnimationCallbackScheduled: boolean = false;
215
- let isDeferredCallbackScheduled: boolean = false;
212
+ let isCallbackScheduled: boolean = false;
213
214
// Keep track of which fibers have captured an error that need to be handled.
215
// Work is removed from this collection after unstable_handleError is called.
@@ -230,20 +227,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
227
let isCommitting: boolean = false;
228
let isUnmounting: boolean = false;
229
233
- function scheduleAnimationCallback(callback) {
234
- if (!isAnimationCallbackScheduled) {
235
- isAnimationCallbackScheduled = true;
236
- hostScheduleAnimationCallback(callback);
237
- }
238
- }
239
-
240
- function scheduleDeferredCallback(callback) {
241
- if (!isDeferredCallbackScheduled) {
242
- isDeferredCallbackScheduled = true;
243
- hostScheduleDeferredCallback(callback);
244
- }
245
- }
246
-
230
function resetContextStack() {
231
// Reset the stack
232
reset();
@@ -748,18 +731,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
731
return next;
732
}
733
751
- function performDeferredWork(deadline) {
752
- // We pass the lowest deferred priority here because it acts as a minimum.
753
- // Higher priorities will also be performed.
754
- isDeferredCallbackScheduled = false;
755
- performWork(OffscreenPriority, deadline);
756
- }
757
-
758
- function performAnimationWork() {
759
- isAnimationCallbackScheduled = false;
760
- performWork(AnimationPriority, null);
761
- }
762
-
734
function clearErrors() {
735
if (nextUnitOfWork === null) {
736
nextUnitOfWork = findNextUnitOfWork();
@@ -788,7 +759,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
759
}
760
}
761
791
- function workLoop(priorityLevel, deadline: Deadline | null) {
762
+ function workLoop(deadline: Deadline | null) {
763
// Clear any errors.
764
clearErrors();
765
@@ -796,12 +767,29 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
767
nextUnitOfWork = findNextUnitOfWork();
768
}
769
799
- // If there's a deadline, and we're not performing Task work, perform work
800
- // using this loop that checks the deadline on every iteration.
801
- if (deadline !== null && priorityLevel > TaskPriority) {
802
- // The deferred work loop will run until there's no time left in
803
- // the current frame.
804
- while (nextUnitOfWork !== null && !deadlineHasExpired) {
770
+ // Flush all synchronous and task work.
771
+ while (
772
+ nextUnitOfWork !== null &&
773
+ nextPriorityLevel !== NoWork &&
774
+ nextPriorityLevel <= TaskPriority
775
+ ) {
776
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
777
+ if (nextUnitOfWork === null) {
778
+ nextUnitOfWork = findNextUnitOfWork();
779
+ // performUnitOfWork returned null, which means we just committed a
780
+ // root. Clear any errors that were scheduled during the commit phase.
781
+ clearErrors();
782
+ }
783
+ }
784
+
785
+ if (deadline !== null) {
786
+ // Flush asynchronous work until the deadline expires.
787
+ while (
788
+ nextUnitOfWork !== null &&
789
+ !deadlineHasExpired &&
790
+ nextPriorityLevel !== NoWork &&
791
+ nextPriorityLevel >= HighPriority
792
+ ) {
793
if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
794
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
795
// In a deferred work batch, iff nextUnitOfWork returns null, we just
@@ -824,30 +812,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
812
deadlineHasExpired = true;
813
}
814
}
827
- } else {
828
- // If there's no deadline, or if we're performing Task work, use this loop
829
- // that doesn't check how much time is remaining. It will keep running
830
- // until we run out of work at this priority level.
831
- while (
832
- nextUnitOfWork !== null &&
833
- nextPriorityLevel !== NoWork &&
834
- nextPriorityLevel <= priorityLevel
835
- ) {
836
- nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
837
- if (nextUnitOfWork === null) {
838
- nextUnitOfWork = findNextUnitOfWork();
839
- // performUnitOfWork returned null, which means we just committed a
840
- // root. Clear any errors that were scheduled during the commit phase.
841
- clearErrors();
842
- }
843
- }
815
}
816
}
817
847
- function performWork(
848
- priorityLevel: PriorityLevel,
849
- deadline: Deadline | null,
850
- ) {
818
+ function performWork(deadline: Deadline | null) {
819
if (__DEV__) {
820
startWorkLoopTimer();
821
}
@@ -858,21 +826,20 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
826
'by a bug in React. Please file an issue.',
827
);
828
isPerformingWork = true;
861
- const isPerformingDeferredWork = !!deadline;
829
+
830
+ let hasRemainingAsyncWork = false;
831
832
// This outer loop exists so that we can restart the work loop after
833
// catching an error. It also lets us flush Task work at the end of a
834
// deferred batch.
866
- while (priorityLevel !== NoWork && !fatalError) {
867
- invariant(
868
- deadline !== null || priorityLevel < HighPriority,
869
- 'Cannot perform deferred work without a deadline. This error is ' +
870
- 'likely caused by a bug in React. Please file an issue.',
871
- );
872
-
835
+ while (fatalError === null) {
836
// Before starting any work, check to see if there are any pending
837
// commits from the previous frame.
838
+ // TODO: Only commit asynchronous priority at beginning or end of a frame.
839
+ // Task work can be committed whenever.
840
if (pendingCommit !== null && !deadlineHasExpired) {
841
+ // Safe to call this outside the work loop because the commit phase has
842
+ // its own try-catch.
843
commitAllWork(pendingCommit);
844
}
845
@@ -882,16 +849,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
849
priorityContextBeforeReconciliation = priorityContext;
850
let error = null;
851
if (__DEV__) {
885
- error = invokeGuardedCallback(
886
- null,
887
- workLoop,
888
- null,
889
- priorityLevel,
890
- deadline,
891
- );
852
+ error = invokeGuardedCallback(null, workLoop, null, deadline);
853
} else {
854
try {
894
- workLoop(priorityLevel, deadline);
855
+ workLoop(deadline);
856
} catch (e) {
857
error = e;
858
}
@@ -913,7 +874,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
874
875
// Complete the boundary as if it rendered null. This will unmount
876
// the failed tree.
916
- beginFailedWork(boundary.alternate, boundary, priorityLevel);
877
+ beginFailedWork(boundary.alternate, boundary, nextPriorityLevel);
878
879
// The next unit of work is now the boundary that captured the error.
880
// Conceptually, we're unwinding the stack. We need to unwind the
@@ -934,49 +895,55 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
895
// inside resetAfterCommit.
896
fatalError = error;
897
}
898
+ } else {
899
+ // There might be work left. Depending on the priority, we should
900
+ // either perform it now or schedule a callback to perform it later.
901
+ switch (nextPriorityLevel) {
902
+ case SynchronousPriority:
903
+ case TaskPriority:
904
+ // We have remaining synchronous or task work. Keep performing it,
905
+ // regardless of whether we're inside a callback.
906
+ continue;
907
+ case HighPriority:
908
+ case LowPriority:
909
+ case OffscreenPriority:
910
+ // We have remaining async work.
911
+ if (deadline === null) {
912
+ // We're not inside a callback. Exit and perform the work during
913
+ // the next callback.
914
+ hasRemainingAsyncWork = true;
915
+ } else {
916
+ // We are inside a callback.
917
+ if (!deadlineHasExpired) {
918
+ // We still have time. Keep working.
919
+ continue;
920
+ }
921
+ // We've run out of time. Exit.
922
+ hasRemainingAsyncWork = true;
923
+ }
924
+ break;
925
+ case NoWork:
926
+ // No work left. We can exit.
927
+ break;
928
+ default:
929
+ invariant(false, 'Switch statement should be exhuastive.');
930
+ }
931
+ // Exit the loop.
932
+ break;
933
}
934
+ }
935
939
- // Stop performing work
940
- priorityLevel = NoWork;
941
-
942
- // If have we more work, and we're in a deferred batch, check to see
943
- // if the deadline has expired.
944
- if (
945
- nextPriorityLevel !== NoWork &&
946
- isPerformingDeferredWork &&
947
- !deadlineHasExpired
948
- ) {
949
- // We have more time to do work.
950
- priorityLevel = nextPriorityLevel;
951
- continue;
952
- }
953
-
954
- // There might be work left. Depending on the priority, we should
955
- // either perform it now or schedule a callback to perform it later.
956
- switch (nextPriorityLevel) {
957
- case SynchronousPriority:
958
- case TaskPriority:
959
- // Perform work immediately by switching the priority level
960
- // and continuing the loop.
961
- priorityLevel = nextPriorityLevel;
962
- break;
963
- case AnimationPriority:
964
- scheduleAnimationCallback(performAnimationWork);
965
- // Even though the next unit of work has animation priority, there
966
- // may still be deferred work left over as well. I think this is
967
- // only important for unit tests. In a real app, a deferred callback
968
- // would be scheduled during the next animation frame.
969
- scheduleDeferredCallback(performDeferredWork);
970
- break;
971
- case HighPriority:
972
- case LowPriority:
973
- case OffscreenPriority:
974
- scheduleDeferredCallback(performDeferredWork);
975
- break;
976
- }
936
+ // If we're inside a callback, set this to false, since we just flushed it.
937
+ if (deadline !== null) {
938
+ isCallbackScheduled = false;
939
+ }
940
+ // If there's remaining async work, make sure we schedule another callback.
941
+ if (hasRemainingAsyncWork && !isCallbackScheduled) {
942
+ scheduleDeferredCallback(performWork);
943
+ isCallbackScheduled = true;
944
}
945
979
- const errorToThrow = fatalError || firstUncaughtError;
946
+ const errorToThrow = fatalError !== null ? fatalError : firstUncaughtError;
947
948
// We're done performing work. Time to clean up.
949
isPerformingWork = false;
@@ -1297,24 +1264,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1264
if (node.tag === HostRoot) {
1265
const root: FiberRoot = (node.stateNode: any);
1266
scheduleRoot(root, priorityLevel);
1300
- // Depending on the priority level, either perform work now or
1301
- // schedule a callback to perform work later.
1302
- switch (priorityLevel) {
1303
- case SynchronousPriority:
1304
- performWork(SynchronousPriority, null);
1305
- return;
1306
- case TaskPriority:
1307
- // TODO: If we're not already performing work, schedule a
1308
- // deferred callback.
1309
- return;
1310
- case AnimationPriority:
1311
- scheduleAnimationCallback(performAnimationWork);
1312
- return;
1313
- case HighPriority:
1314
- case LowPriority:
1315
- case OffscreenPriority:
1316
- scheduleDeferredCallback(performDeferredWork);
1317
- return;
1267
+ if (priorityLevel === SynchronousPriority) {
1268
+ performWork(null);
1269
+ } else if (priorityLevel !== NoWork && !isCallbackScheduled) {
1270
+ scheduleDeferredCallback(performWork);
1271
+ isCallbackScheduled = true;
1272
}
1273
} else {
1274
if (__DEV__) {
@@ -1381,7 +1335,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1335
// If we're not already inside a batch, we need to flush any task work
1336
// that was created by the user-provided function.
1337
if (!isPerformingWork && !isBatchingUpdates) {
1384
- performWork(TaskPriority, null);
1338
+ performWork(null);
1339
}
1340
}
1341
}
src/renderers/shared/fiber/ReactPriorityLevel.js
+4
-5
@@ -12,14 +12,13 @@
12
13
'use strict';
14
15
-export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
15
+export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;
16
17
module.exports = {
18
NoWork: 0, // No work is pending.
19
SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects.
20
TaskPriority: 2, // Completes at the end of the current tick.
21
- AnimationPriority: 3, // Needs to complete before the next frame.
22
- HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
23
- LowPriority: 5, // Data fetching, or result from updating stores.
24
- OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
21
+ HighPriority: 3, // Interaction that needs to complete pretty soon to feel responsive.
22
+ LowPriority: 4, // Data fetching, or result from updating stores.
23
+ OffscreenPriority: 5, // Won't be visible but do the work in case it becomes visible.
24
};
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+4
-9
@@ -320,29 +320,25 @@ describe('ReactIncremental', () => {
320
}
321
322
// Init
323
- ReactNoop.performAnimationWork(() => {
323
+ ReactNoop.syncUpdates(() => {
324
ReactNoop.render(<Foo text="foo" />);
325
});
326
ReactNoop.flush();
327
-
327
expect(ops).toEqual(['Foo', 'Bar', 'Bar', 'Middle', 'Middle']);
328
329
ops = [];
330
331
// Render the high priority work (everying except the hidden trees).
333
- ReactNoop.performAnimationWork(() => {
332
+ ReactNoop.syncUpdates(() => {
333
ReactNoop.render(<Foo text="foo" />);
334
});
336
- ReactNoop.flushAnimationPri();
337
-
335
expect(ops).toEqual(['Foo', 'Bar', 'Bar']);
336
337
ops = [];
338
339
// The hidden content was deprioritized from high to low priority. A low
340
// priority callback should have been scheduled. Flush it now.
344
- ReactNoop.flushDeferredPri();
345
-
341
+ ReactNoop.flush();
342
expect(ops).toEqual(['Middle', 'Middle']);
343
});
344
@@ -769,10 +765,9 @@ describe('ReactIncremental', () => {
765
// Interrupt the current low pri work with a high pri update elsewhere in
766
// the tree.
767
ops = [];
772
- ReactNoop.performAnimationWork(() => {
768
+ ReactNoop.syncUpdates(() => {
769
sibling.setState({});
770
});
775
- ReactNoop.flushAnimationPri();
771
expect(ops).toEqual(['Sibling']);
772
773
// Continue the low pri work. The work on Child and GrandChild was memoized
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
-80
@@ -109,49 +109,6 @@ describe('ReactIncrementalErrorHandling', () => {
109
expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
110
});
111
112
- it('catches render error in a boundary during animation mounting', () => {
113
- var ops = [];
114
- class ErrorBoundary extends React.Component {
115
- state = {error: null};
116
- unstable_handleError(error) {
117
- ops.push('ErrorBoundary unstable_handleError');
118
- this.setState({error});
119
- }
120
- render() {
121
- if (this.state.error) {
122
- ops.push('ErrorBoundary render error');
123
- return (
124
- <span prop={`Caught an error: ${this.state.error.message}.`} />
125
- );
126
- }
127
- ops.push('ErrorBoundary render success');
128
- return this.props.children;
129
- }
130
- }
131
-
132
- function BrokenRender(props) {
133
- ops.push('BrokenRender');
134
- throw new Error('Hello');
135
- }
136
-
137
- ReactNoop.performAnimationWork(() => {
138
- ReactNoop.render(
139
- <ErrorBoundary>
140
- <BrokenRender />
141
- </ErrorBoundary>,
142
- );
143
- });
144
-
145
- ReactNoop.flushAnimationPri();
146
- expect(ops).toEqual([
147
- 'ErrorBoundary render success',
148
- 'BrokenRender',
149
- 'ErrorBoundary unstable_handleError',
150
- 'ErrorBoundary render error',
151
- ]);
152
- expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
153
- });
154
-
112
it('catches render error in a boundary during synchronous mounting', () => {
113
var ops = [];
114
class ErrorBoundary extends React.Component {
@@ -316,43 +273,6 @@ describe('ReactIncrementalErrorHandling', () => {
273
expect(ReactNoop.getChildren()).toEqual([]);
274
});
275
319
- it('propagates an error from a noop error boundary during animation mounting', () => {
320
- var ops = [];
321
- class RethrowErrorBoundary extends React.Component {
322
- unstable_handleError(error) {
323
- ops.push('RethrowErrorBoundary unstable_handleError');
324
- throw error;
325
- }
326
- render() {
327
- ops.push('RethrowErrorBoundary render');
328
- return this.props.children;
329
- }
330
- }
331
-
332
- function BrokenRender() {
333
- ops.push('BrokenRender');
334
- throw new Error('Hello');
335
- }
336
-
337
- ReactNoop.performAnimationWork(() => {
338
- ReactNoop.render(
339
- <RethrowErrorBoundary>
340
- <BrokenRender />
341
- </RethrowErrorBoundary>,
342
- );
343
- });
344
-
345
- expect(() => {
346
- ReactNoop.flush();
347
- }).toThrow('Hello');
348
- expect(ops).toEqual([
349
- 'RethrowErrorBoundary render',
350
- 'BrokenRender',
351
- 'RethrowErrorBoundary unstable_handleError',
352
- ]);
353
- expect(ReactNoop.getChildren()).toEqual([]);
354
- });
355
-
276
it('propagates an error from a noop error boundary during synchronous mounting', () => {
277
var ops = [];
278
class RethrowErrorBoundary extends React.Component {
src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js
+3
-4
@@ -281,7 +281,8 @@ describe('ReactDebugFiberPerf', () => {
281
});
282
283
it('measures deprioritized work', () => {
284
- ReactNoop.performAnimationWork(() => {
284
+ addComment('Flush the parent');
285
+ ReactNoop.syncUpdates(() => {
286
ReactNoop.render(
287
<Parent>
288
<div hidden={true}>
@@ -290,10 +291,8 @@ describe('ReactDebugFiberPerf', () => {
291
</Parent>,
292
);
293
});
293
- addComment('Flush the parent');
294
- ReactNoop.flushAnimationPri();
294
addComment('Flush the child');
296
- ReactNoop.flushDeferredPri();
295
+ ReactNoop.flush();
296
expect(getFlameChart()).toMatchSnapshot();
297
});
298
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
+66
-112
@@ -36,16 +36,6 @@ describe('ReactIncrementalScheduling', () => {
36
expect(ReactNoop.getChildren()).toEqual([span('1')]);
37
});
38
39
- it('schedules and flushes animation work', () => {
40
- ReactNoop.performAnimationWork(() => {
41
- ReactNoop.render(<span prop="1" />);
42
- });
43
- expect(ReactNoop.getChildren()).toEqual([]);
44
-
45
- ReactNoop.flushAnimationPri();
46
- expect(ReactNoop.getChildren()).toEqual([span('1')]);
47
- });
48
-
39
it('searches for work on other roots once the current root completes', () => {
40
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
41
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
@@ -58,46 +48,23 @@ describe('ReactIncrementalScheduling', () => {
48
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
49
});
50
61
- it('schedules an animation callback when there`\s leftover animation work', () => {
62
- class Foo extends React.Component {
63
- state = {step: 0};
64
- componentDidMount() {
65
- ReactNoop.performAnimationWork(() => {
66
- this.setState({step: 2});
67
- });
68
- this.setState({step: 1});
69
- }
70
- render() {
71
- return <span prop={this.state.step} />;
72
- }
73
- }
74
-
75
- ReactNoop.render(<Foo />);
76
- // Flush just enough work to mount the component, but not enough to flush
77
- // the animation update.
78
- ReactNoop.flushDeferredPri(25);
79
- expect(ReactNoop.getChildren()).toEqual([span(1)]);
80
-
81
- // There's more animation work. A callback should have been scheduled.
82
- ReactNoop.flushAnimationPri();
83
- expect(ReactNoop.getChildren()).toEqual([span(2)]);
84
- });
85
-
51
it('schedules top-level updates in order of priority', () => {
52
// Initial render.
53
ReactNoop.render(<span prop={1} />);
54
ReactNoop.flush();
55
expect(ReactNoop.getChildren()).toEqual([span(1)]);
56
92
- ReactNoop.render(<span prop={5} />);
93
- ReactNoop.performAnimationWork(() => {
94
- ReactNoop.render(<span prop={2} />);
95
- ReactNoop.render(<span prop={3} />);
96
- ReactNoop.render(<span prop={4} />);
57
+ ReactNoop.batchedUpdates(() => {
58
+ ReactNoop.render(<span prop={5} />);
59
+ ReactNoop.syncUpdates(() => {
60
+ ReactNoop.render(<span prop={2} />);
61
+ ReactNoop.render(<span prop={3} />);
62
+ ReactNoop.render(<span prop={4} />);
63
+ });
64
});
65
66
// The low pri update should be flushed last, even though it was scheduled
100
- // before the animation updates.
67
+ // before the sync updates.
68
ReactNoop.flush();
69
expect(ReactNoop.getChildren()).toEqual([span(5)]);
70
});
@@ -207,78 +174,69 @@ describe('ReactIncrementalScheduling', () => {
174
]);
175
});
176
210
- it('can opt-in to deferred/animation scheduling inside componentDidMount/Update', () => {
211
- var instance;
212
- var ops = [];
213
-
177
+ it('can opt-in to async scheduling inside componentDidMount/Update', () => {
178
+ let instance;
179
class Foo extends React.Component {
180
state = {tick: 0};
181
182
componentDidMount() {
218
- ReactNoop.performAnimationWork(() => {
219
- ops.push('componentDidMount (before setState): ' + this.state.tick);
183
+ ReactNoop.deferredUpdates(() => {
184
+ ReactNoop.yield(
185
+ 'componentDidMount (before setState): ' + this.state.tick,
186
+ );
187
this.setState({tick: 1});
221
- ops.push('componentDidMount (after setState): ' + this.state.tick);
188
+ ReactNoop.yield(
189
+ 'componentDidMount (after setState): ' + this.state.tick,
190
+ );
191
});
192
}
193
194
componentDidUpdate() {
226
- ReactNoop.performAnimationWork(() => {
227
- ops.push('componentDidUpdate: ' + this.state.tick);
195
+ ReactNoop.deferredUpdates(() => {
196
+ ReactNoop.yield('componentDidUpdate: ' + this.state.tick);
197
if (this.state.tick === 2) {
229
- ops.push(
198
+ ReactNoop.yield(
199
'componentDidUpdate (before setState): ' + this.state.tick,
200
);
201
this.setState({tick: 3});
233
- ops.push('componentDidUpdate (after setState): ' + this.state.tick);
202
+ ReactNoop.yield(
203
+ 'componentDidUpdate (after setState): ' + this.state.tick,
204
+ );
205
}
206
});
207
}
208
209
render() {
239
- ops.push('render: ' + this.state.tick);
210
+ ReactNoop.yield('render: ' + this.state.tick);
211
instance = this;
212
return <span prop={this.state.tick} />;
213
}
214
}
215
245
- ReactNoop.render(<Foo />);
246
-
247
- ReactNoop.flushDeferredPri(20 + 5);
248
- expect(ops).toEqual([
249
- 'render: 0',
250
- 'componentDidMount (before setState): 0',
251
- 'componentDidMount (after setState): 0',
252
- // Following items shouldn't appear because they are the result of an
253
- // update scheduled with animation priority
254
- // 'render: 1',
255
- // 'componentDidUpdate: 1',
256
- ]);
257
-
258
- ops = [];
216
+ ReactNoop.syncUpdates(() => {
217
+ ReactNoop.render(<Foo />);
218
+ });
219
+ // The cDM update should not have flushed yet because it has async priority.
220
+ expect(ReactNoop.getChildren()).toEqual([span(0)]);
221
260
- ReactNoop.flushAnimationPri();
261
- expect(ops).toEqual(['render: 1', 'componentDidUpdate: 1']);
222
+ // Now flush the cDM update.
223
+ expect(ReactNoop.flush()).toEqual(['render: 1', 'componentDidUpdate: 1']);
224
+ expect(ReactNoop.getChildren()).toEqual([span(1)]);
225
263
- ops = [];
226
+ // Increment the tick to 2. This will trigger an update inside cDU. Flush
227
+ // the first update without flushing the second one.
228
instance.setState({tick: 2});
265
- ReactNoop.flushDeferredPri(20 + 5);
266
-
267
- expect(ops).toEqual([
229
+ ReactNoop.flushThrough([
230
'render: 2',
231
'componentDidUpdate: 2',
232
'componentDidUpdate (before setState): 2',
233
'componentDidUpdate (after setState): 2',
272
- // Following items shouldn't appear because they are the result of an
273
- // update scheduled with animation priority
274
- // 'render: 3',
275
- // 'componentDidUpdate: 3',
234
]);
235
+ expect(ReactNoop.getChildren()).toEqual([span(2)]);
236
278
- ops = [];
279
-
280
- ReactNoop.flushAnimationPri();
281
- expect(ops).toEqual(['render: 3', 'componentDidUpdate: 3']);
237
+ // Now flush the cDU update.
238
+ expect(ReactNoop.flush()).toEqual(['render: 3', 'componentDidUpdate: 3']);
239
+ expect(ReactNoop.getChildren()).toEqual([span(3)]);
240
});
241
242
it('performs Task work even after time runs out', () => {
@@ -309,37 +267,6 @@ describe('ReactIncrementalScheduling', () => {
267
expect(ReactNoop.getChildren()).toEqual([span(5)]);
268
});
269
312
- it('does not perform animation work after time runs out', () => {
313
- class Foo extends React.Component {
314
- state = {step: 1};
315
- componentDidMount() {
316
- ReactNoop.performAnimationWork(() => {
317
- this.setState({step: 2}, () => {
318
- this.setState({step: 3}, () => {
319
- this.setState({step: 4}, () => {
320
- this.setState({step: 5});
321
- });
322
- });
323
- });
324
- });
325
- }
326
- render() {
327
- return <span prop={this.state.step} />;
328
- }
329
- }
330
- ReactNoop.render(<Foo />);
331
- // This should be just enough to complete all the work, but not enough to
332
- // commit it.
333
- ReactNoop.flushDeferredPri(20);
334
- expect(ReactNoop.getChildren()).toEqual([]);
335
-
336
- // Do one more unit of work.
337
- ReactNoop.flushDeferredPri(10);
338
- // None of the updates should be flushed because they only have
339
- // animation priority.
340
- expect(ReactNoop.getChildren()).toEqual([span(1)]);
341
- });
342
-
270
it('can opt-out of batching using unbatchedUpdates', () => {
271
// syncUpdates gives synchronous priority to updates
272
ReactNoop.syncUpdates(() => {
@@ -406,4 +333,31 @@ describe('ReactIncrementalScheduling', () => {
333
'componentDidUpdate: 2',
334
]);
335
});
336
+
337
+ it('updates do not schedule a new callback if already inside a callback', () => {
338
+ class Foo extends React.Component {
339
+ state = {foo: 'foo'};
340
+ componentWillReceiveProps() {
341
+ ReactNoop.yield(
342
+ 'has callback before setState: ' + ReactNoop.hasScheduledCallback(),
343
+ );
344
+ this.setState({foo: 'baz'});
345
+ ReactNoop.yield(
346
+ 'has callback after setState: ' + ReactNoop.hasScheduledCallback(),
347
+ );
348
+ }
349
+ render() {
350
+ return null;
351
+ }
352
+ }
353
+
354
+ ReactNoop.render(<Foo step={1} />);
355
+ ReactNoop.flush();
356
+
357
+ ReactNoop.render(<Foo step={2} />);
358
+ expect(ReactNoop.flush()).toEqual([
359
+ 'has callback before setState: false',
360
+ 'has callback after setState: false',
361
+ ]);
362
+ });
363
});
src/renderers/shared/fiber/__tests__/ReactIncrementalUpdates-test.js
+73
-92
@@ -24,13 +24,17 @@ describe('ReactIncrementalUpdates', () => {
24
ReactFeatureFlags.disableNewFiberFeatures = false;
25
});
26
27
+ function span(prop) {
28
+ return {type: 'span', children: [], prop};
29
+ }
30
+
31
it('applies updates in order of priority', () => {
32
let state;
33
class Foo extends React.Component {
34
state = {};
35
componentDidMount() {
32
- ReactNoop.performAnimationWork(() => {
33
- // Has Animation priority
36
+ ReactNoop.deferredUpdates(() => {
37
+ // Has low priority
38
this.setState({b: 'b'});
39
this.setState({c: 'c'});
40
});
@@ -92,18 +96,23 @@ describe('ReactIncrementalUpdates', () => {
96
ReactNoop.render(<Foo />);
97
ReactNoop.flush();
98
95
- instance.setState({x: 'x'});
96
- instance.setState({y: 'y'});
97
- ReactNoop.performAnimationWork(() => {
98
- instance.setState({a: 'a'});
99
- instance.setState({b: 'b'});
99
+ ReactNoop.syncUpdates(() => {
100
+ ReactNoop.batchedUpdates(() => {
101
+ ReactNoop.deferredUpdates(() => {
102
+ instance.setState({x: 'x'});
103
+ instance.setState({y: 'y'});
104
+ });
105
+ instance.setState({a: 'a'});
106
+ instance.setState({b: 'b'});
107
+ ReactNoop.deferredUpdates(() => {
108
+ instance.updater.enqueueReplaceState(instance, {c: 'c'});
109
+ instance.setState({d: 'd'});
110
+ });
111
+ });
112
});
101
- instance.updater.enqueueReplaceState(instance, {c: 'c'});
102
- instance.setState({d: 'd'});
113
104
- ReactNoop.flushAnimationPri();
114
// Even though a replaceState has been already scheduled, it hasn't been
106
- // flushed yet because it has low priority.
115
+ // flushed yet because it has async priority.
116
expect(instance.state).toEqual({a: 'a', b: 'b'});
117
expect(ops).toEqual([
118
'render',
@@ -115,141 +124,112 @@ describe('ReactIncrementalUpdates', () => {
124
ops = [];
125
126
ReactNoop.flush();
118
- // Now the rest of the updates are flushed.
127
+ // Now the rest of the updates are flushed, including the replaceState.
128
expect(instance.state).toEqual({c: 'c', d: 'd'});
129
expect(ops).toEqual(['render', 'componentDidUpdate']);
130
});
131
132
it('can abort an update, schedule additional updates, and resume', () => {
133
let instance;
125
- let ops = [];
134
class Foo extends React.Component {
135
state = {};
128
- componentDidUpdate() {
129
- ops.push('componentDidUpdate');
130
- }
136
render() {
132
- ops.push('render');
137
instance = this;
134
- return <div />;
138
+ return <span prop={Object.keys(this.state).sort().join('')} />;
139
}
140
}
141
142
ReactNoop.render(<Foo />);
143
ReactNoop.flush();
144
141
- ops = [];
142
-
143
- let progressedUpdates = [];
145
function createUpdate(letter) {
146
return () => {
146
- progressedUpdates.push(letter);
147
+ ReactNoop.yield(letter);
148
return {
149
[letter]: letter,
150
};
151
};
152
}
153
154
+ // Schedule some async updates
155
instance.setState(createUpdate('a'));
156
instance.setState(createUpdate('b'));
157
instance.setState(createUpdate('c'));
158
157
- // Do just enough work to begin the update but not enough to flush it
158
- ReactNoop.flushDeferredPri(15);
159
- // expect(ReactNoop.getChildren()).toEqual([span('')]);
160
- expect(ops).toEqual(['render']);
161
- expect(progressedUpdates).toEqual(['a', 'b', 'c']);
162
- expect(instance.state).toEqual({a: 'a', b: 'b', c: 'c'});
163
-
164
- ops = [];
165
- progressedUpdates = [];
166
-
167
- instance.setState(createUpdate('f'));
168
- ReactNoop.performAnimationWork(() => {
169
- instance.setState(createUpdate('d'));
170
- instance.setState(createUpdate('e'));
159
+ // Begin the updates but don't flush them yet
160
+ ReactNoop.flushThrough(['a', 'b', 'c']);
161
+ expect(ReactNoop.getChildren()).toEqual([span('')]);
162
+
163
+ // Schedule some more updates at different priorities
164
+ ReactNoop.batchedUpdates(() => {
165
+ instance.setState(createUpdate('f'));
166
+ ReactNoop.syncUpdates(() => {
167
+ instance.setState(createUpdate('d'));
168
+ instance.setState(createUpdate('e'));
169
+ });
170
+ instance.setState(createUpdate('g'));
171
});
172
- instance.setState(createUpdate('g'));
172
174
- ReactNoop.flushAnimationPri();
175
- expect(ops).toEqual([
176
- // Flushes animation work (d and e)
177
- 'render',
178
- 'componentDidUpdate',
179
- ]);
180
- ops = [];
181
- ReactNoop.flush();
182
- expect(ops).toEqual([
183
- // Flushes deferred work (f and g)
184
- 'render',
185
- 'componentDidUpdate',
186
- ]);
187
- expect(progressedUpdates).toEqual(['d', 'e', 'a', 'b', 'c', 'f', 'g']);
188
- expect(instance.state).toEqual({
189
- a: 'a',
190
- b: 'b',
191
- c: 'c',
192
- d: 'd',
193
- e: 'e',
194
- f: 'f',
195
- g: 'g',
196
- });
173
+ // The sync updates should have flushed, but not the async ones
174
+ expect(ReactNoop.getChildren()).toEqual([span('de')]);
175
+
176
+ // Now flush the remaining work. Updates a, b, and c should be processed
177
+ // again, since they were interrupted last time.
178
+ expect(ReactNoop.flush()).toEqual(['a', 'b', 'c', 'f', 'g']);
179
+ expect(ReactNoop.getChildren()).toEqual([span('abcdefg')]);
180
});
181
182
it('can abort an update, schedule a replaceState, and resume', () => {
183
let instance;
201
- let ops = [];
184
class Foo extends React.Component {
185
state = {};
204
- componentDidUpdate() {
205
- ops.push('componentDidUpdate');
206
- }
186
render() {
208
- ops.push('render');
187
instance = this;
210
- return <span />;
188
+ return <span prop={Object.keys(this.state).sort().join('')} />;
189
}
190
}
191
192
ReactNoop.render(<Foo />);
193
ReactNoop.flush();
216
- ops = [];
194
218
- let progressedUpdates = [];
195
function createUpdate(letter) {
196
return () => {
221
- progressedUpdates.push(letter);
197
+ ReactNoop.yield(letter);
198
return {
199
[letter]: letter,
200
};
201
};
202
}
203
204
+ // Schedule some async updates
205
instance.setState(createUpdate('a'));
206
instance.setState(createUpdate('b'));
207
instance.setState(createUpdate('c'));
208
232
- // Do just enough work to begin the update but not enough to flush it
233
- ReactNoop.flushDeferredPri(20);
234
- expect(ops).toEqual(['render']);
235
- expect(progressedUpdates).toEqual(['a', 'b', 'c']);
236
- expect(instance.state).toEqual({a: 'a', b: 'b', c: 'c'});
237
-
238
- ops = [];
239
- progressedUpdates = [];
240
-
241
- instance.setState(createUpdate('f'));
242
- ReactNoop.performAnimationWork(() => {
243
- instance.setState(createUpdate('d'));
244
- // No longer a public API, but we can test that it works internally by
245
- // reaching into the updater.
246
- instance.updater.enqueueReplaceState(instance, createUpdate('e'));
209
+ // Begin the updates but don't flush them yet
210
+ ReactNoop.flushThrough(['a', 'b', 'c']);
211
+ expect(ReactNoop.getChildren()).toEqual([span('')]);
212
+
213
+ // Schedule some more updates at different priorities
214
+ ReactNoop.batchedUpdates(() => {
215
+ instance.setState(createUpdate('f'));
216
+ ReactNoop.syncUpdates(() => {
217
+ instance.setState(createUpdate('d'));
218
+ // No longer a public API, but we can test that it works internally by
219
+ // reaching into the updater.
220
+ instance.updater.enqueueReplaceState(instance, createUpdate('e'));
221
+ });
222
+ instance.setState(createUpdate('g'));
223
});
248
- instance.setState(createUpdate('g'));
224
250
- ReactNoop.flush();
251
- expect(progressedUpdates).toEqual(['d', 'e', 'f', 'g']);
252
- expect(instance.state).toEqual({e: 'e', f: 'f', g: 'g'});
225
+ // The sync updates should have flushed, but not the async ones. Update d
226
+ // was dropped and replaced by e.
227
+ expect(ReactNoop.getChildren()).toEqual([span('e')]);
228
+
229
+ // Now flush the remaining work. Updates a, b, and c should be processed
230
+ // again, since they were interrupted last time.
231
+ expect(ReactNoop.flush()).toEqual(['a', 'b', 'c', 'f', 'g']);
232
+ expect(ReactNoop.getChildren()).toEqual([span('abcefg')]);
233
});
234
235
it('passes accumulation of previous updates to replaceState updater function', () => {
@@ -327,11 +307,12 @@ describe('ReactIncrementalUpdates', () => {
307
308
ops = [];
309
330
- ReactNoop.performAnimationWork(() => {
331
- instance.setState({a: 'a'});
332
- ReactNoop.render(<Foo />); // Trigger componentWillReceiveProps
310
+ ReactNoop.batchedUpdates(() => {
311
+ ReactNoop.syncUpdates(() => {
312
+ instance.setState({a: 'a'});
313
+ ReactNoop.render(<Foo />); // Trigger componentWillReceiveProps
314
+ });
315
});
334
- ReactNoop.flush();
316
317
expect(instance.state).toEqual({a: 'a', b: 'b'});
318
expect(ops).toEqual(['componentWillReceiveProps', 'render']);
src/renderers/shared/fiber/__tests__/__snapshots__/ReactIncrementalPerf-test.js.snap
-2
@@ -85,8 +85,6 @@ exports[`ReactDebugFiberPerf does not treat setState from cWM or cWRP as cascadi
85
⚛ (Committing Changes)
86
⚛ (Committing Host Effects: 2 Total)
87
⚛ (Calling Lifecycle Methods: 2 Total)
88
-
89
-⚛ (React Tree Reconciliation)
88
"
89
`;
90
src/renderers/testing/ReactTestRendererFiberEntry.js
-4
@@ -219,10 +219,6 @@ var TestRenderer = ReactFiberReconciler({
219
removeChild: removeChild,
220
removeChildFromContainer: removeChild,
221
222
- scheduleAnimationCallback(fn: Function): void {
223
- setTimeout(fn);
224
- },
225
-
222
scheduleDeferredCallback(fn: Function): void {
223
setTimeout(fn, 0, {timeRemaining: Infinity});
224
},
yarn.lock
+3
-47
@@ -90,7 +90,7 @@ ansi-escapes@^1.1.0, ansi-escapes@^1.4.0:
90
version "1.4.0"
91
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
92
93
-ansi-regex@^2.0.0, ansi-regex@^2.1.1:
93
+ansi-regex@^2.0.0:
94
version "2.1.1"
95
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
96
@@ -1745,7 +1745,7 @@ devtools-timeline-model@1.1.6:
1745
chrome-devtools-frontend "1.0.401423"
1746
resolve "1.1.7"
1747
1748
-diff@^3.0.0, diff@^3.2.0:
1748
+diff@^3.0.0:
1749
version "3.2.0"
1750
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
1751
@@ -3199,15 +3199,6 @@ jest-diff@^19.0.0:
3199
jest-matcher-utils "^19.0.0"
3200
pretty-format "^19.0.0"
3201
3202
-jest-diff@^20.0.3:
3203
- version "20.0.3"
3204
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617"
3205
- dependencies:
3206
- chalk "^1.1.3"
3207
- diff "^3.2.0"
3208
- jest-matcher-utils "^20.0.3"
3209
- pretty-format "^20.0.3"
3210
-
3202
jest-environment-jsdom@^19.0.2:
3203
version "19.0.2"
3204
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-19.0.2.tgz#ceda859c4a4b94ab35e4de7dab54b926f293e4a3"
@@ -3254,14 +3245,7 @@ jest-matcher-utils@^19.0.0:
3245
chalk "^1.1.3"
3246
pretty-format "^19.0.0"
3247
3257
-jest-matcher-utils@^20.0.3:
3258
- version "20.0.3"
3259
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612"
3260
- dependencies:
3261
- chalk "^1.1.3"
3262
- pretty-format "^20.0.3"
3263
-
3264
-jest-matchers@^19.0.0:
3248
+jest-matchers@^19.0.0, jest-matchers@^19.0.1:
3249
version "19.0.0"
3250
resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-19.0.0.tgz#c74ecc6ebfec06f384767ba4d6fa4a42d6755754"
3251
dependencies:
@@ -3270,15 +3254,6 @@ jest-matchers@^19.0.0:
3254
jest-message-util "^19.0.0"
3255
jest-regex-util "^19.0.0"
3256
3273
-jest-matchers@^20.0.3:
3274
- version "20.0.3"
3275
- resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60"
3276
- dependencies:
3277
- jest-diff "^20.0.3"
3278
- jest-matcher-utils "^20.0.3"
3279
- jest-message-util "^20.0.3"
3280
- jest-regex-util "^20.0.3"
3281
-
3257
jest-message-util@^19.0.0:
3258
version "19.0.0"
3259
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-19.0.0.tgz#721796b89c0e4d761606f9ba8cb828a3b6246416"
@@ -3286,14 +3261,6 @@ jest-message-util@^19.0.0:
3261
chalk "^1.1.1"
3262
micromatch "^2.3.11"
3263
3289
-jest-message-util@^20.0.3:
3290
- version "20.0.3"
3291
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c"
3292
- dependencies:
3293
- chalk "^1.1.3"
3294
- micromatch "^2.3.11"
3295
- slash "^1.0.0"
3296
-
3264
jest-mock@^19.0.0:
3265
version "19.0.0"
3266
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-19.0.0.tgz#67038641e9607ab2ce08ec4a8cb83aabbc899d01"
@@ -3302,10 +3269,6 @@ jest-regex-util@^19.0.0:
3269
version "19.0.0"
3270
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-19.0.0.tgz#b7754587112aede1456510bb1f6afe74ef598691"
3271
3305
-jest-regex-util@^20.0.3:
3306
- version "20.0.3"
3307
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762"
3308
-
3272
jest-resolve-dependencies@^19.0.0:
3273
version "19.0.0"
3274
resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-19.0.0.tgz#a741ad1fa094140e64ecf2642a504f834ece22ee"
@@ -4356,13 +4319,6 @@ pretty-format@^19.0.0:
4319
dependencies:
4320
ansi-styles "^3.0.0"
4321
4359
-pretty-format@^20.0.3:
4360
- version "20.0.3"
4361
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14"
4362
- dependencies:
4363
- ansi-regex "^2.1.1"
4364
- ansi-styles "^3.0.0"
4365
-
4322
private@^0.1.6, private@~0.1.5:
4323
version "0.1.7"
4324
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"