@samitouri / QOS-React / commits / 812244b57a

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"