@samitouri / QOS-React-2 / commits / 0b37588171

Revert "[Fiber] Queue top-level updates"

Andrew Clark committed Dec 19, 2016 at 15:32 UTC 0b37588171aa454eae4a31b499550377fff0c185
14 files changed +505 -213
scripts/fiber/tests-failing.txt
+1
@@ -90,6 +90,7 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js
90 * throws in setState if the update callback is not a function
91 * throws in replaceState if the update callback is not a function
92 * throws in forceUpdate if the update callback is not a function
93 +* unmounts and remounts a root in the same batch
94
95 src/renderers/shared/shared/__tests__/refs-test.js
96 * Should increase refs with an increase in divs
scripts/fiber/tests-passing.txt
+19 -6
@@ -1190,13 +1190,27 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalReflection-test.js
1190 * finds no node before insertion and correct node before deletion
1191
1192 src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
1193 -* schedules and flushes deferred work
1193 * schedules and flushes animation work
1195 -* searches for work on other roots once the current root completes
1196 -* schedules an animation callback when there`s leftover animation work
1197 -* schedules top-level updates in order of priority
1198 -* schedules top-level updates with same priority in order of insertion
1194 +* schedules and flushes animation work for many roots
1195 +* flushes all scheduled animation work
1196 +* flushes all scheduled animation work for many roots
1197 +* schedules and flushes deferred work
1198 +* schedules and flushes deferred work for many roots
1199 +* flushes scheduled deferred work fitting within deadline
1200 +* flushes scheduled deferred work fitting within deadline for many roots
1201 +* schedules more deferred work if it runs out of time
1202 +* schedules more deferred work if it runs out of time with many roots
1203 +* flushes late animation work in a deferred callback if it wins
1204 +* flushes late animation work in a deferred callback if it wins with many roots
1205 +* flushes late animation work in an animation callback if it wins
1206 +* flushes late animation work in an animation callback if it wins with many roots
1207 +* flushes all work in a deferred callback if it wins
1208 +* flushes all work in a deferred callback if it wins with many roots
1209 +* flushes root with late deferred work in an animation callback if it wins
1210 +* flushes all roots with animation work in an animation callback if it wins
1211 +* splits deferred work on multiple roots
1212 * works on deferred roots in the order they were scheduled
1213 +* handles interleaved deferred and animation work
1214 * schedules sync updates when inside componentDidMount/Update
1215 * can opt-in to deferred/animation scheduling inside componentDidMount/Update
1216 * performs Task work even after time runs out
@@ -1559,7 +1573,6 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js
1573 * does not update one component twice in a batch (#2410)
1574 * does not update one component twice in a batch (#6371)
1575 * unstable_batchedUpdates should return value from a callback
1562 -* unmounts and remounts a root in the same batch
1576
1577 src/renderers/shared/shared/__tests__/refs-destruction-test.js
1578 * should remove refs when destroying the parent
src/renderers/art/ReactARTFiber.js
+1 -5
@@ -357,11 +357,7 @@ class Surface extends Component {
357 }
358
359 componentWillUnmount() {
360 - ARTRenderer.updateContainer(
361 - null,
362 - this._mountNode,
363 - this,
364 - );
360 + ARTRenderer.unmountContainer(this._mountNode);
361 }
362
363 render() {
src/renderers/dom/fiber/ReactDOMFiber.js
+9 -5
@@ -259,7 +259,7 @@ function warnAboutUnstableUse() {
259 warned = true;
260 }
261
262 -function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any, any>, children : ReactNodeList, containerNode : DOMContainerElement | Document, callback: ?Function) {
262 +function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any, any>, element : ReactElement<any>, containerNode : DOMContainerElement | Document, callback: ?Function) {
263 let container : DOMContainerElement =
264 containerNode.nodeType === DOCUMENT_NODE ? (containerNode : any).documentElement : (containerNode : any);
265 let root;
@@ -268,9 +268,9 @@ function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any,
268 while (container.lastChild) {
269 container.removeChild(container.lastChild);
270 }
271 - root = container._reactRootContainer = DOMRenderer.mountContainer(children, container, parentComponent, callback);
271 + root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, parentComponent, callback);
272 } else {
273 - DOMRenderer.updateContainer(children, root = container._reactRootContainer, parentComponent, callback);
273 + DOMRenderer.updateContainer(element, root = container._reactRootContainer, parentComponent, callback);
274 }
275 return DOMRenderer.getPublicRootInstance(root);
276 }
@@ -292,9 +292,13 @@ var ReactDOM = {
292
293 unmountComponentAtNode(container : DOMContainerElement) {
294 warnAboutUnstableUse();
295 - return renderSubtreeIntoContainer(null, null, container, () => {
295 + const root = container._reactRootContainer;
296 + if (root) {
297 + // TODO: Is it safe to reset this now or should I wait since this
298 + // unmount could be deferred?
299 container._reactRootContainer = null;
297 - });
300 + DOMRenderer.unmountContainer(root);
301 + }
302 },
303
304 findDOMNode: findDOMNode,
src/renderers/native/ReactNativeFiber.js
+3 -4
@@ -329,7 +329,7 @@ const NativeRenderer = ReactFiberReconciler({
329 // But creates an additional child Fiber for raw text children.
330 // No additional native views are created though.
331 // It's not clear to me which is better so I'm deferring for now.
332 - // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
332 + // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
333 return false;
334 },
335
@@ -375,9 +375,8 @@ const ReactNative = {
375 const root = roots.get(containerTag);
376 if (root) {
377 // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
378 - NativeRenderer.updateContainer(null, root, null, () => {
379 - roots.delete(containerTag);
380 - });
378 + roots.delete(containerTag);
379 + NativeRenderer.unmountContainer(root);
380 }
381 },
382
src/renderers/noop/ReactNoop.js
+5 -8
@@ -198,11 +198,10 @@ var ReactNoop = {
198
199 unmountRootWithID(rootID : string) {
200 const root = roots.get(rootID);
201 + roots.delete(rootID);
202 + rootContainers.delete(rootID);
203 if (root) {
202 - NoopRenderer.updateContainer(null, root, null, () => {
203 - roots.delete(rootID);
204 - rootContainers.delete(rootID);
205 - });
204 + NoopRenderer.unmountContainer(root);
205 }
206 },
207
@@ -305,16 +304,14 @@ var ReactNoop = {
304 log(
305 ' '.repeat(depth + 1) + '~',
306 firstUpdate && firstUpdate.partialState,
308 - firstUpdate.callback ? 'with callback' : '',
309 - '[' + firstUpdate.priorityLevel + ']'
307 + firstUpdate.callback ? 'with callback' : ''
308 );
309 var next;
310 while (next = firstUpdate.next) {
311 log(
312 ' '.repeat(depth + 1) + '~',
313 next.partialState,
316 - next.callback ? 'with callback' : '',
317 - '[' + firstUpdate.priorityLevel + ']'
314 + next.callback ? 'with callback' : ''
315 );
316 }
317 }
src/renderers/shared/fiber/ReactFiberBeginWork.js
+4 -7
@@ -542,16 +542,13 @@ module.exports = function<T, P, I, TI, C, CX>(
542 pushTopLevelContextObject(root.context, false);
543 }
544
545 - pushHostContainer(root.containerInfo);
546 -
545 if (updateQueue) {
548 - const prevState = workInProgress.memoizedState;
549 - const state = beginUpdateQueue(workInProgress, updateQueue, null, prevState, null, priorityLevel);
550 - const element = state.element;
551 - reconcileChildren(current, workInProgress, element);
552 - workInProgress.memoizedState = state;
546 + beginUpdateQueue(workInProgress, updateQueue, null, null, null, priorityLevel);
547 }
548
549 + pushHostContainer(workInProgress.stateNode.containerInfo);
550 + reconcileChildren(current, workInProgress, pendingProps);
551 +
552 // A yield component is just a placeholder, we can just run through the
553 // next one immediately.
554 return workInProgress.child;
src/renderers/shared/fiber/ReactFiberReconciler.js
+43 -17
@@ -15,7 +15,6 @@
15 import type { Fiber } from 'ReactFiber';
16 import type { FiberRoot } from 'ReactFiberRoot';
17 import type { PriorityLevel } from 'ReactPriorityLevel';
18 -import type { ReactNodeList } from 'ReactTypes';
18
19 var {
20 findCurrentUnmaskedContext,
@@ -71,8 +70,9 @@ export type HostConfig<T, P, I, TI, C, CX> = {
70 };
71
72 export type Reconciler<C, I, TI> = {
74 - mountContainer(element : ReactNodeList, containerInfo : C, parentComponent : ?ReactComponent<any, any, any>) : OpaqueNode,
75 - updateContainer(element : ReactNodeList, container : OpaqueNode, parentComponent : ?ReactComponent<any, any, any>) : void,
73 + mountContainer(element : ReactElement<any>, containerInfo : C, parentComponent : ?ReactComponent<any, any, any>) : OpaqueNode,
74 + updateContainer(element : ReactElement<any>, container : OpaqueNode, parentComponent : ?ReactComponent<any, any, any>) : void,
75 + unmountContainer(container : OpaqueNode) : void,
76 performWithPriority(priorityLevel : PriorityLevel, fn : Function) : void,
77 /* eslint-disable no-undef */
78 // FIXME: ESLint complains about type parameter
@@ -98,7 +98,7 @@ getContextForSubtree._injectFiber(function(fiber : Fiber) {
98 module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C, CX>) : Reconciler<C, I, TI> {
99
100 var {
101 - scheduleTopLevelSetState,
101 + scheduleWork,
102 scheduleUpdateCallback,
103 performWithPriority,
104 batchedUpdates,
@@ -108,16 +108,25 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
108
109 return {
110
111 - mountContainer(element : ReactNodeList, containerInfo : C, parentComponent : ?ReactComponent<any, any, any>, callback: ?Function) : OpaqueNode {
111 + mountContainer(element : ReactElement<any>, containerInfo : C, parentComponent : ?ReactComponent<any, any, any>, callback: ?Function) : OpaqueNode {
112 const context = getContextForSubtree(parentComponent);
113 const root = createFiberRoot(containerInfo, context);
114 const current = root.current;
115
116 - scheduleTopLevelSetState(current, { element });
116 + // TODO: Use the updateQueue and scheduleUpdate, instead of pendingProps.
117 + // TODO: This should not override the pendingWorkPriority if there is
118 + // higher priority work in the subtree.
119 +
120 + current.pendingProps = element;
121 + if (current.alternate) {
122 + current.alternate.pendingProps = element;
123 + }
124 if (callback) {
125 scheduleUpdateCallback(current, callback);
126 }
127
128 + scheduleWork(root);
129 +
130 if (__DEV__ && ReactFiberInstrumentation.debugTool) {
131 ReactFiberInstrumentation.debugTool.onMountContainer(root);
132 }
@@ -128,27 +137,44 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
137 return current;
138 },
139
131 - updateContainer(element : ReactNodeList, container : OpaqueNode, parentComponent : ?ReactComponent<any, any, any>, callback: ?Function) : void {
140 + updateContainer(element : ReactElement<any>, container : OpaqueNode, parentComponent : ?ReactComponent<any, any, any>, callback: ?Function) : void {
141 // TODO: If this is a nested container, this won't be the root.
142 const root : FiberRoot = (container.stateNode : any);
143 const current = root.current;
144
145 root.pendingContext = getContextForSubtree(parentComponent);
146
138 - scheduleTopLevelSetState(current, { element });
147 + // TODO: Use the updateQueue and scheduleUpdate, instead of pendingProps.
148 + // TODO: This should not override the pendingWorkPriority if there is
149 + // higher priority work in the subtree.
150 + current.pendingProps = element;
151 + if (current.alternate) {
152 + current.alternate.pendingProps = element;
153 + }
154 if (callback) {
155 scheduleUpdateCallback(current, callback);
156 }
157
143 - if (__DEV__) {
144 - if (ReactFiberInstrumentation.debugTool) {
145 - if (element === null) {
146 - ReactFiberInstrumentation.debugTool.onUpdateContainer(root);
147 - } else {
148 - // This is an unmount
149 - ReactFiberInstrumentation.debugTool.onUnmountContainer(root);
150 - }
151 - }
158 + scheduleWork(root);
159 +
160 + if (__DEV__ && ReactFiberInstrumentation.debugTool) {
161 + ReactFiberInstrumentation.debugTool.onUpdateContainer(root);
162 + }
163 + },
164 +
165 + unmountContainer(container : OpaqueNode) : void {
166 + // TODO: If this is a nested container, this won't be the root.
167 + const root : FiberRoot = (container.stateNode : any);
168 + // TODO: Use pending work/state instead of props.
169 + root.current.pendingProps = [];
170 + if (root.current.alternate) {
171 + root.current.alternate.pendingProps = [];
172 + }
173 +
174 + scheduleWork(root);
175 +
176 + if (__DEV__ && ReactFiberInstrumentation.debugTool) {
177 + ReactFiberInstrumentation.debugTool.onUnmountContainer(root);
178 }
179 },
180
src/renderers/shared/fiber/ReactFiberScheduler.js
+48 -36
@@ -59,7 +59,6 @@ var {
59 addReplaceUpdate,
60 addForceUpdate,
61 addCallback,
62 - addTopLevelUpdate,
62 } = require('ReactFiberUpdateQueue');
63
64 var {
@@ -945,10 +944,28 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
944 }
945 }
946
948 - function scheduleRoot(root : FiberRoot) {
949 - const priorityLevel = root.current.pendingWorkPriority;
950 - if (priorityLevel === NoWork) {
951 - return;
947 + function scheduleWork(root : FiberRoot) {
948 + let priorityLevel = priorityContext;
949 +
950 + // If we're in a batch, switch to task priority
951 + if (priorityLevel === SynchronousPriority && isPerformingWork) {
952 + priorityLevel = TaskPriority;
953 + }
954 +
955 + scheduleWorkAtPriority(root, priorityLevel);
956 + }
957 +
958 + function scheduleWorkAtPriority(root : FiberRoot, priorityLevel : PriorityLevel) {
959 + // Set the priority on the root, without deprioritizing
960 + if (root.current.pendingWorkPriority === NoWork ||
961 + priorityLevel <= root.current.pendingWorkPriority) {
962 + root.current.pendingWorkPriority = priorityLevel;
963 + }
964 + if (root.current.alternate) {
965 + if (root.current.alternate.pendingWorkPriority === NoWork ||
966 + priorityLevel <= root.current.alternate.pendingWorkPriority) {
967 + root.current.alternate.pendingWorkPriority = priorityLevel;
968 + }
969 }
970
971 if (!root.isScheduled) {
@@ -970,6 +987,30 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
987 // priority work somewhere earlier than before.
988 nextUnitOfWork = null;
989 }
990 +
991 + // Depending on the priority level, either perform work now or schedule
992 + // a callback to perform work later.
993 + switch (priorityLevel) {
994 + case SynchronousPriority:
995 + // Perform work immediately
996 + performWork(SynchronousPriority);
997 + return;
998 + case TaskPriority:
999 + // If we're already performing work, Task work will be flushed before
1000 + // exiting the current batch. So we can skip it here.
1001 + if (!isPerformingWork) {
1002 + performWork(TaskPriority);
1003 + }
1004 + return;
1005 + case AnimationPriority:
1006 + scheduleAnimationCallback(performAnimationWork);
1007 + return;
1008 + case HighPriority:
1009 + case LowPriority:
1010 + case OffscreenPriority:
1011 + scheduleDeferredCallback(performDeferredWork);
1012 + return;
1013 + }
1014 }
1015
1016 function scheduleUpdateAtPriority(fiber : Fiber, priorityLevel : PriorityLevel) {
@@ -1002,30 +1043,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1043 if (!node.return) {
1044 if (node.tag === HostRoot) {
1045 const root : FiberRoot = (node.stateNode : any);
1005 - scheduleRoot(root, priorityLevel);
1006 - // Depending on the priority level, either perform work now or
1007 - // schedule a callback to perform work later.
1008 - switch (priorityLevel) {
1009 - case SynchronousPriority:
1010 - // Perform work immediately
1011 - performWork(SynchronousPriority);
1012 - return;
1013 - case TaskPriority:
1014 - // If we're already performing work, Task work will be flushed before
1015 - // exiting the current batch. So we can skip it here.
1016 - if (!isPerformingWork) {
1017 - performWork(TaskPriority);
1018 - }
1019 - return;
1020 - case AnimationPriority:
1021 - scheduleAnimationCallback(performAnimationWork);
1022 - return;
1023 - case HighPriority:
1024 - case LowPriority:
1025 - case OffscreenPriority:
1026 - scheduleDeferredCallback(performDeferredWork);
1027 - return;
1028 - }
1046 + scheduleWorkAtPriority(root, priorityLevel);
1047 } else {
1048 // TODO: Warn about setting state on an unmounted component.
1049 return;
@@ -1059,12 +1077,6 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1077 scheduleUpdateAtPriority(fiber, priorityContext);
1078 }
1079
1062 - // TODO: This indirection will be removed as part of #8585
1063 - function scheduleTopLevelSetState(fiber : Fiber, partialState : any) {
1064 - addTopLevelUpdate(fiber, partialState, priorityContext);
1065 - scheduleUpdateAtPriority(fiber, priorityContext);
1066 - }
1067 -
1080 function performWithPriority(priorityLevel : PriorityLevel, fn : Function) {
1081 const previousPriorityContext = priorityContext;
1082 priorityContext = priorityLevel;
@@ -1112,7 +1124,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1124 }
1125
1126 return {
1115 - scheduleTopLevelSetState: scheduleTopLevelSetState,
1127 + scheduleWork: scheduleWork,
1128 scheduleUpdateCallback: scheduleUpdateCallback,
1129 performWithPriority: performWithPriority,
1130 batchedUpdates: batchedUpdates,
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+6 -55
@@ -39,7 +39,6 @@ type Update = {
39 callback: Callback | null,
40 isReplace: boolean,
41 isForced: boolean,
42 - isTopLevelUnmount: boolean,
42 next: Update | null,
43 };
44
@@ -142,7 +141,6 @@ function cloneUpdate(update : Update) : Update {
141 callback: update.callback,
142 isReplace: update.isReplace,
143 isForced: update.isForced,
145 - isTopLevelUnmount: update.isTopLevelUnmount,
144 next: null,
145 };
146 }
@@ -211,9 +209,7 @@ function findInsertionPosition(queue, update) : Update | null {
209 //
210 // However, if incoming update is inserted into the same position of both lists,
211 // we shouldn't make a copy.
214 -//
215 -// If the update is cloned, it returns the cloned update.
216 -function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : Update | null {
212 +function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : void {
213 const queue1 = ensureUpdateQueue(fiber);
214 const queue2 = fiber.alternate ? ensureUpdateQueue(fiber.alternate) : null;
215
@@ -248,7 +244,7 @@ function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : Up
244 if (!queue2) {
245 // If there's no alternate queue, there's nothing else to do but insert.
246 insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1);
251 - return null;
247 + return;
248 }
249
250 // If there is an alternate queue, find the insertion position.
@@ -264,7 +260,6 @@ function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : Up
260 // insert the clone into the alternate queue.
261 const update2 = cloneUpdate(update);
262 insertUpdateIntoQueue(queue2, update2, insertAfter2, insertBefore2);
267 - return update2;
263 } else {
264 // The insertion positions are the same, so when we inserted into the first
265 // queue, it also inserted into the alternate. All we need to do is update
@@ -277,8 +272,6 @@ function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : Up
272 queue2.last = null;
273 }
274 }
280 -
281 - return null;
275 }
276
277 function addUpdate(
@@ -292,7 +285,6 @@ function addUpdate(
285 callback: null,
286 isReplace: false,
287 isForced: false,
295 - isTopLevelUnmount: false,
288 next: null,
289 };
290 if (__DEV__) {
@@ -314,7 +306,6 @@ function addReplaceUpdate(
306 callback: null,
307 isReplace: true,
308 isForced: false,
317 - isTopLevelUnmount: false,
309 next: null,
310 };
311
@@ -333,7 +324,6 @@ function addForceUpdate(fiber : Fiber, priorityLevel : PriorityLevel) : void {
324 callback: null,
325 isReplace: false,
326 isForced: true,
336 - isTopLevelUnmount: false,
327 next: null,
328 };
329 if (__DEV__) {
@@ -352,7 +342,6 @@ function addCallback(fiber : Fiber, callback: Callback, priorityLevel : Priority
342 callback,
343 isReplace: false,
344 isForced: false,
355 - isTopLevelUnmount: false,
345 next: null,
346 };
347 insertUpdate(fiber, update);
@@ -364,42 +353,6 @@ function getPendingPriority(queue : UpdateQueue) : PriorityLevel {
353 }
354 exports.getPendingPriority = getPendingPriority;
355
367 -function addTopLevelUpdate(
368 - fiber : Fiber,
369 - partialState : PartialState<any, any>,
370 - priorityLevel : PriorityLevel
371 -) : void {
372 - const isTopLevelUnmount = partialState === null;
373 -
374 - const update = {
375 - priorityLevel,
376 - partialState,
377 - callback: null,
378 - isReplace: false,
379 - isForced: false,
380 - isTopLevelUnmount,
381 - next: null,
382 - };
383 - const update2 = insertUpdate(fiber, update);
384 -
385 - if (isTopLevelUnmount) {
386 - // Drop all updates that are lower-priority, so that the tree is not
387 - // remounted. We need to do this for both queues.
388 - const queue1 = fiber.updateQueue;
389 - const queue2 = fiber.alternate && fiber.alternate.updateQueue;
390 -
391 - if (queue1 && update.next) {
392 - update.next = null;
393 - queue1.last = update;
394 - }
395 - if (queue2 && update2 && update2.next) {
396 - update2.next = null;
397 - queue2.last = update;
398 - }
399 - }
400 -}
401 -exports.addTopLevelUpdate = addTopLevelUpdate;
402 -
356 function getStateFromUpdate(update, instance, prevState, props) {
357 const partialState = update.partialState;
358 if (typeof partialState === 'function') {
@@ -459,14 +412,12 @@ function beginUpdateQueue(
412 if (update.isForced) {
413 queue.hasForceUpdate = true;
414 }
462 - // Second condition ignores top-level unmount callbacks if they are not the
463 - // last update in the queue, since a subsequent update will cause a remount.
464 - if (update.callback && !(update.isTopLevelUnmount && update.next)) {
465 - const callbackUpdate = cloneUpdate(update);
415 + if (update.callback) {
416 if (callbackList && callbackList.last) {
467 - callbackList.last.next = callbackUpdate;
468 - callbackList.last = callbackUpdate;
417 + callbackList.last.next = update;
418 + callbackList.last = update;
419 } else {
420 + const callbackUpdate = cloneUpdate(update);
421 callbackList = {
422 first: callbackUpdate,
423 last: callbackUpdate,
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+1
@@ -267,6 +267,7 @@ describe('ReactIncremental', () => {
267 ReactNoop.performAnimationWork(() => {
268 ReactNoop.render(<Foo text="foo" />);
269 });
270 + ReactNoop.render(<Foo text="bar" />);
271 ReactNoop.flushAnimationPri();
272
273 expect(ops).toEqual(['Foo', 'Bar', 'Bar']);
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
-1
@@ -654,7 +654,6 @@ describe('ReactIncrementalErrorHandling', () => {
654 ReactNoop.unmountRootWithID('d');
655 ReactNoop.unmountRootWithID('e');
656 ReactNoop.unmountRootWithID('f');
657 - ReactNoop.flush();
657 expect(ReactNoop.getChildren('a')).toEqual(null);
658 expect(ReactNoop.getChildren('b')).toEqual(null);
659 expect(ReactNoop.getChildren('c')).toEqual(null);
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
+357 -51
@@ -25,93 +25,351 @@ describe('ReactIncrementalScheduling', () => {
25 return { type: 'span', children: [], prop };
26 }
27
28 - it('schedules and flushes deferred work', () => {
29 - ReactNoop.render(<span prop="1" />);
28 + it('schedules and flushes animation work', () => {
29 + ReactNoop.performAnimationWork(() => {
30 + ReactNoop.render(<span prop="1" />);
31 + });
32 expect(ReactNoop.getChildren()).toEqual([]);
33
32 - ReactNoop.flushDeferredPri();
34 + ReactNoop.flushAnimationPri();
35 expect(ReactNoop.getChildren()).toEqual([span('1')]);
36 });
37
36 - it('schedules and flushes animation work', () => {
38 + it('schedules and flushes animation work for many roots', () => {
39 + ReactNoop.performAnimationWork(() => {
40 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
41 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
42 + ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
43 + });
44 + expect(ReactNoop.getChildren('a')).toEqual([]);
45 + expect(ReactNoop.getChildren('b')).toEqual([]);
46 + expect(ReactNoop.getChildren('c')).toEqual([]);
47 +
48 + ReactNoop.flushAnimationPri();
49 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
50 + expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
51 + expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
52 + });
53 +
54 + it('flushes all scheduled animation work', () => {
55 ReactNoop.performAnimationWork(() => {
56 ReactNoop.render(<span prop="1" />);
57 });
58 + ReactNoop.performAnimationWork(() => {
59 + ReactNoop.render(<span prop="2" />);
60 + });
61 expect(ReactNoop.getChildren()).toEqual([]);
62
63 ReactNoop.flushAnimationPri();
64 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
65 + });
66 +
67 + it('flushes all scheduled animation work for many roots', () => {
68 + ReactNoop.performAnimationWork(() => {
69 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
70 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
71 + ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
72 + });
73 + ReactNoop.performAnimationWork(() => {
74 + ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
75 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
76 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
77 + });
78 + expect(ReactNoop.getChildren('a')).toEqual([]);
79 + expect(ReactNoop.getChildren('b')).toEqual([]);
80 + expect(ReactNoop.getChildren('c')).toEqual([]);
81 +
82 + ReactNoop.flushAnimationPri();
83 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
84 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
85 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
86 + });
87 +
88 + it('schedules and flushes deferred work', () => {
89 + ReactNoop.render(<span prop="1" />);
90 + expect(ReactNoop.getChildren()).toEqual([]);
91 +
92 + ReactNoop.flushDeferredPri();
93 expect(ReactNoop.getChildren()).toEqual([span('1')]);
94 });
95
46 - it('searches for work on other roots once the current root completes', () => {
96 + it('schedules and flushes deferred work for many roots', () => {
97 ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
98 ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
99 ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
100 + expect(ReactNoop.getChildren('a')).toEqual([]);
101 + expect(ReactNoop.getChildren('b')).toEqual([]);
102 + expect(ReactNoop.getChildren('c')).toEqual([]);
103
51 - ReactNoop.flush();
52 -
104 + ReactNoop.flushDeferredPri();
105 expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
106 expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
107 expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
108 });
109
58 - it('schedules an animation callback when there`\s leftover animation work', () => {
59 - class Foo extends React.Component {
60 - state = { step: 0 };
61 - componentDidMount() {
62 - ReactNoop.performAnimationWork(() => {
63 - this.setState({ step: 2 });
64 - });
65 - this.setState({ step: 1 });
66 - }
67 - render() {
68 - return <span prop={this.state.step} />;
69 - }
70 - }
110 + it('flushes scheduled deferred work fitting within deadline', () => {
111 + ReactNoop.render(<span prop="1" />);
112 + ReactNoop.render(<span prop="2" />);
113 + expect(ReactNoop.getChildren()).toEqual([]);
114
72 - ReactNoop.render(<Foo />);
73 - // Flush just enough work to mount the component, but not enough to flush
74 - // the animation update.
75 - ReactNoop.flushDeferredPri(25);
76 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
115 + ReactNoop.flushDeferredPri();
116 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
117 + });
118 +
119 + it('flushes scheduled deferred work fitting within deadline for many roots', () => {
120 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
121 + ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
122 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
123 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
124 + ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
125 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
126 + expect(ReactNoop.getChildren('a')).toEqual([]);
127 + expect(ReactNoop.getChildren('b')).toEqual([]);
128 + expect(ReactNoop.getChildren('c')).toEqual([]);
129 +
130 + ReactNoop.flushDeferredPri();
131 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
132 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
133 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
134 + });
135 +
136 + it('schedules more deferred work if it runs out of time', () => {
137 + ReactNoop.render(<span prop="1" />);
138 + ReactNoop.render(<span prop="2" />);
139 + expect(ReactNoop.getChildren()).toEqual([]);
140 +
141 + ReactNoop.flushDeferredPri(5);
142 + expect(ReactNoop.getChildren()).toEqual([]);
143 +
144 + ReactNoop.flushDeferredPri(10);
145 + expect(ReactNoop.getChildren()).toEqual([]);
146 +
147 + ReactNoop.flushDeferredPri(10 + 5);
148 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
149 + });
150 +
151 + it('schedules more deferred work if it runs out of time with many roots', () => {
152 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
153 + ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
154 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
155 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
156 + ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
157 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
158 + expect(ReactNoop.getChildren('a')).toEqual([]);
159 + expect(ReactNoop.getChildren('b')).toEqual([]);
160 + expect(ReactNoop.getChildren('c')).toEqual([]);
161
78 - // There's more animation work. A callback should have been scheduled.
162 + ReactNoop.flushDeferredPri(15 + 5);
163 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
164 + expect(ReactNoop.getChildren('b')).toEqual([]);
165 + expect(ReactNoop.getChildren('c')).toEqual([]);
166 +
167 + ReactNoop.flushDeferredPri(15 + 5);
168 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
169 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
170 + expect(ReactNoop.getChildren('c')).toEqual([]);
171 +
172 + ReactNoop.flushDeferredPri(15 + 5);
173 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
174 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
175 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
176 + });
177 +
178 + it('flushes late animation work in a deferred callback if it wins', () => {
179 + // Schedule early deferred
180 + ReactNoop.render(<span prop="1" />);
181 + // Schedule late animation
182 + ReactNoop.performAnimationWork(() => {
183 + ReactNoop.render(<span prop="2" />);
184 + });
185 + expect(ReactNoop.getChildren()).toEqual([]);
186 +
187 + // We only scheduled deferred callback so that's what we get.
188 + // It will flush everything.
189 + ReactNoop.flushDeferredPri();
190 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
191 + });
192 +
193 + it('flushes late animation work in a deferred callback if it wins with many roots', () => {
194 + // Schedule early deferred
195 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
196 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
197 + // Schedule late animation
198 + ReactNoop.performAnimationWork(() => {
199 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
200 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
201 + });
202 + expect(ReactNoop.getChildren('a')).toEqual([]);
203 + expect(ReactNoop.getChildren('b')).toEqual([]);
204 + expect(ReactNoop.getChildren('c')).toEqual([]);
205 +
206 + // We only scheduled deferred callback so that's what we get.
207 + // It will flush everything.
208 + ReactNoop.flushDeferredPri();
209 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
210 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
211 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
212 + });
213 +
214 + it('flushes late animation work in an animation callback if it wins', () => {
215 + // Schedule early deferred
216 + ReactNoop.render(<span prop="1" />);
217 + // Schedule late animation
218 + ReactNoop.performAnimationWork(() => {
219 + ReactNoop.render(<span prop="2" />);
220 + });
221 + expect(ReactNoop.getChildren()).toEqual([]);
222 +
223 + // Flushing animation should have flushed the animation.
224 ReactNoop.flushAnimationPri();
80 - expect(ReactNoop.getChildren()).toEqual([span(2)]);
225 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
226 });
227
83 - it('schedules top-level updates in order of priority', () => {
84 - // Initial render.
85 - ReactNoop.render(<span prop={1} />);
86 - ReactNoop.flush();
87 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
228 + it('flushes late animation work in an animation callback if it wins with many roots', () => {
229 + // Schedule early deferred
230 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
231 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
232 + // Schedule late animation
233 + ReactNoop.performAnimationWork(() => {
234 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
235 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
236 + });
237 + expect(ReactNoop.getChildren('a')).toEqual([]);
238 + expect(ReactNoop.getChildren('b')).toEqual([]);
239 + expect(ReactNoop.getChildren('c')).toEqual([]);
240
89 - ReactNoop.render(<span prop={5} />);
241 + // Flushing animation should have flushed the animation.
242 + ReactNoop.flushAnimationPri();
243 + expect(ReactNoop.getChildren('a')).toEqual([]);
244 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
245 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
246 +
247 + ReactNoop.flushDeferredPri();
248 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
249 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
250 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
251 + });
252 +
253 + it('flushes all work in a deferred callback if it wins', () => {
254 + // Schedule early animation
255 ReactNoop.performAnimationWork(() => {
91 - ReactNoop.render(<span prop={2} />);
92 - ReactNoop.render(<span prop={3} />);
93 - ReactNoop.render(<span prop={4} />);
256 + ReactNoop.render(<span prop="1" />);
257 });
258 + // Schedule late deferred
259 + ReactNoop.render(<span prop="2" />);
260 + expect(ReactNoop.getChildren()).toEqual([]);
261
96 - // The low pri update should be flushed last, even though it was scheduled
97 - // before the animation updates.
98 - ReactNoop.flush();
99 - expect(ReactNoop.getChildren()).toEqual([span(5)]);
262 + // Flushing deferred should have flushed both early animation and late deferred work that invalidated it.
263 + // This is not a common case, as animation should generally be flushed before deferred work.
264 + ReactNoop.flushDeferredPri();
265 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
266 });
267
102 - it('schedules top-level updates with same priority in order of insertion', () => {
103 - // Initial render.
104 - ReactNoop.render(<span prop={1} />);
105 - ReactNoop.flush();
106 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
268 + it('flushes all work in a deferred callback if it wins with many roots', () => {
269 + // Schedule early animation
270 + ReactNoop.performAnimationWork(() => {
271 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
272 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
273 + });
274 + // Schedule late deferred
275 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
276 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
277 + expect(ReactNoop.getChildren('a')).toEqual([]);
278 + expect(ReactNoop.getChildren('b')).toEqual([]);
279 + expect(ReactNoop.getChildren('c')).toEqual([]);
280
108 - ReactNoop.render(<span prop={2} />);
109 - ReactNoop.render(<span prop={3} />);
110 - ReactNoop.render(<span prop={4} />);
111 - ReactNoop.render(<span prop={5} />);
281 + // Flushing deferred should have flushed both early animation and late deferred work that invalidated it.
282 + // This is not a common case, as animation should generally be flushed before deferred work.
283 + ReactNoop.flushDeferredPri();
284 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
285 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
286 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
287 + });
288
113 - ReactNoop.flush();
114 - expect(ReactNoop.getChildren()).toEqual([span(5)]);
289 + it('flushes root with late deferred work in an animation callback if it wins', () => {
290 + // Schedule early animation
291 + ReactNoop.performAnimationWork(() => {
292 + ReactNoop.render(<span prop="1" />);
293 + });
294 + // Schedule late deferred
295 + ReactNoop.render(<span prop="2" />);
296 + expect(ReactNoop.getChildren()).toEqual([]);
297 +
298 + // Flushing animation work flushes everything on this root.
299 + ReactNoop.flushAnimationPri();
300 + expect(ReactNoop.getChildren()).toEqual([span('2')]);
301 + });
302 +
303 + it('flushes all roots with animation work in an animation callback if it wins', () => {
304 + // Schedule early animation
305 + ReactNoop.performAnimationWork(() => {
306 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
307 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
308 + });
309 + // Schedule late deferred
310 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
311 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
312 + expect(ReactNoop.getChildren('a')).toEqual([]);
313 + expect(ReactNoop.getChildren('b')).toEqual([]);
314 + expect(ReactNoop.getChildren('c')).toEqual([]);
315 +
316 + // Flushing animation work flushes all roots with animation work.
317 + ReactNoop.flushAnimationPri();
318 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
319 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
320 + expect(ReactNoop.getChildren('c')).toEqual([]);
321 +
322 + // Flushing deferred work flushes the root with only deferred work.
323 + ReactNoop.flushDeferredPri();
324 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
325 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
326 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
327 + });
328 +
329 + it('splits deferred work on multiple roots', () => {
330 + // Schedule one root
331 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
332 + ReactNoop.flushDeferredPri(15 + 5);
333 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
334 +
335 + // Schedule two roots
336 + ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
337 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
338 + // First scheduled one gets processed first
339 + ReactNoop.flushDeferredPri(15 + 5);
340 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
341 + expect(ReactNoop.getChildren('b')).toEqual([]);
342 + expect(ReactNoop.getChildren('c')).toEqual(null);
343 + // Then the second one gets processed
344 + ReactNoop.flushDeferredPri(15 + 5);
345 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
346 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
347 + expect(ReactNoop.getChildren('c')).toEqual(null);
348 +
349 + // Schedule three roots
350 + ReactNoop.renderToRootWithID(<span prop="a:3" />, 'a');
351 + ReactNoop.renderToRootWithID(<span prop="b:3" />, 'b');
352 + ReactNoop.renderToRootWithID(<span prop="c:3" />, 'c');
353 + // They get processed in the order they were scheduled
354 + ReactNoop.flushDeferredPri(15 + 5);
355 + expect(ReactNoop.getChildren('a')).toEqual([span('a:3')]);
356 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
357 + expect(ReactNoop.getChildren('c')).toEqual([]);
358 + ReactNoop.flushDeferredPri(15 + 5);
359 + expect(ReactNoop.getChildren('a')).toEqual([span('a:3')]);
360 + expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
361 + expect(ReactNoop.getChildren('c')).toEqual([]);
362 + ReactNoop.flushDeferredPri(15 + 5);
363 + expect(ReactNoop.getChildren('a')).toEqual([span('a:3')]);
364 + expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
365 + expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]);
366 +
367 + // Schedule one root many times
368 + ReactNoop.renderToRootWithID(<span prop="a:4" />, 'a');
369 + ReactNoop.renderToRootWithID(<span prop="a:5" />, 'a');
370 + ReactNoop.renderToRootWithID(<span prop="a:6" />, 'a');
371 + ReactNoop.flushDeferredPri(15 + 5);
372 + expect(ReactNoop.getChildren('a')).toEqual([span('a:6')]);
373 });
374
375 it('works on deferred roots in the order they were scheduled', () => {
@@ -144,6 +402,54 @@ describe('ReactIncrementalScheduling', () => {
402 expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
403 });
404
405 + it('handles interleaved deferred and animation work', () => {
406 + ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
407 + ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
408 + ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
409 + ReactNoop.flush();
410 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
411 + expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
412 + expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
413 +
414 + // Schedule both deferred and animation work
415 + ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
416 + ReactNoop.performAnimationWork(() => {
417 + ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
418 + ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
419 + });
420 + // We're flushing deferred work
421 + // Still, roots with animation work are handled first
422 + ReactNoop.flushDeferredPri(15);
423 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
424 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
425 + expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
426 + ReactNoop.flushDeferredPri(15);
427 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
428 + expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
429 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
430 +
431 + // More deferred and animation work just got scheduled!
432 + ReactNoop.renderToRootWithID(<span prop="c:3" />, 'c');
433 + ReactNoop.performAnimationWork(() => {
434 + ReactNoop.renderToRootWithID(<span prop="b:3" />, 'b');
435 + });
436 + // Animation is still handled first
437 + ReactNoop.flushDeferredPri(15);
438 + expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
439 + expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
440 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
441 +
442 + // Finally we handle deferred root in the order it was scheduled
443 + ReactNoop.flushDeferredPri(15 + 5);
444 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
445 + expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
446 + expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
447 + ReactNoop.flushDeferredPri(15 + 5);
448 + expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
449 + expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
450 + expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]);
451 + });
452 +
453 it('schedules sync updates when inside componentDidMount/Update', () => {
454 var instance;
455 var ops = [];
src/renderers/shared/shared/__tests__/ReactCompositeComponentState-test.js
+8 -18
@@ -169,22 +169,16 @@ describe('ReactCompositeComponent-state', () => {
169 // componentDidMount() called setState({color:'yellow'}), which is async.
170 // The update doesn't happen until the next flush.
171 ['componentDidMount-end', 'orange'],
172 - ['setState-sunrise', 'orange'],
173 - ['setState-orange', 'orange'],
172 ];
173
176 - // In Fiber, the initial callback is not enqueued until after any work
177 - // scheduled by lifecycles has flushed (same semantics as a regular setState
178 - // callback outside of a batch). In Stack, the initial render is scheduled
179 - // inside of batchedUpdates, so the callback gets flushed right after
180 - // componentDidMount.
181 - // TODO: We should fix this, in both Stack and Fiber, so that the behavior
182 - // is consistent regardless of whether you're in a batch.
183 - if (!ReactDOMFeatureFlags.useFiber) {
184 - expected.push(['initial-callback', 'orange']);
185 - }
186 -
187 - expected.push(['shouldComponentUpdate-currentState', 'orange'],
174 + // The setState callbacks in componentWillMount, and the initial callback
175 + // passed to ReactDOM.render, should be flushed right after component
176 + // did mount:
177 + expected.push(
178 + ['setState-sunrise', 'orange'], // 1
179 + ['setState-orange', 'orange'], // 2
180 + ['initial-callback', 'orange'], // 3
181 + ['shouldComponentUpdate-currentState', 'orange'],
182 ['shouldComponentUpdate-nextState', 'yellow'],
183 ['componentWillUpdate-currentState', 'orange'],
184 ['componentWillUpdate-nextState', 'yellow'],
@@ -194,10 +188,6 @@ describe('ReactCompositeComponent-state', () => {
188 ['setState-yellow', 'yellow'],
189 );
190
197 - if (ReactDOMFeatureFlags.useFiber) {
198 - expected.push(['initial-callback', 'yellow']);
199 - }
200 -
191 expected.push(
192 ['componentWillReceiveProps-start', 'yellow'],
193 // setState({color:'green'}) only enqueues a pending state.