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.