Separate priority for updates
When resetting the priority in the complete phase, check the priority of the update queue so that updates aren't dropped. Updates inside render, child cWRP, etc are no longer dropped. The next step is sort the queue by priority and only flush updates that match the current priority level.
Andrew Clark committed
Dec 9, 2016 at 15:06 UTC
94011e98a6c731061de100c487773cc03869e986
8 files changed
+187
-93
scripts/fiber/tests-failing.txt
-5
@@ -71,13 +71,8 @@ src/renderers/shared/shared/__tests__/ReactComponentLifeCycle-test.js
71
* should carry through each of the phases of setup
72
73
src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
74
-* should warn about `setState` in render
75
-* should warn about `setState` in getChildContext
74
* should update refs if shouldComponentUpdate gives false
75
78
-src/renderers/shared/shared/__tests__/ReactCompositeComponentState-test.js
79
-* should update state when called from child cWRP
80
-
76
src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
77
* should still throw when rendering to undefined
78
* throws when rendering null at the top level
scripts/fiber/tests-passing-except-dev.txt
+2
@@ -114,6 +114,8 @@ src/renderers/shared/shared/__tests__/ReactComponentLifeCycle-test.js
114
src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
115
* should warn about `forceUpdate` on unmounted components
116
* should warn about `setState` on unmounted components
117
+* should warn about `setState` in render
118
+* should warn about `setState` in getChildContext
119
* should disallow nested render calls
120
121
src/renderers/shared/shared/__tests__/ReactMultiChild-test.js
scripts/fiber/tests-passing.txt
+1
@@ -1382,6 +1382,7 @@ src/renderers/shared/shared/__tests__/ReactCompositeComponentState-test.js
1382
* should support setting state
1383
* should call componentDidUpdate of children first
1384
* should batch unmounts
1385
+* should update state when called from child cWRP
1386
1387
src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
1388
* should not produce child DOM nodes for null and false
src/renderers/shared/fiber/ReactFiberBeginWork.js
+12
-13
@@ -71,7 +71,8 @@ if (__DEV__) {
71
module.exports = function<T, P, I, TI, C, CX>(
72
config : HostConfig<T, P, I, TI, C, CX>,
73
hostContext : HostContext<C, CX>,
74
- scheduleUpdate : (fiber: Fiber) => void
74
+ scheduleUpdateAtPriority : (fiber: Fiber, priorityLevel : PriorityLevel) => void,
75
+ getPriorityContext : () => PriorityLevel
76
) {
77
78
const { shouldSetTextContent } = config;
@@ -88,7 +89,7 @@ module.exports = function<T, P, I, TI, C, CX>(
89
mountClassInstance,
90
resumeMountClassInstance,
91
updateClassInstance,
91
- } = ReactFiberClassComponent(scheduleUpdate);
92
+ } = ReactFiberClassComponent(scheduleUpdateAtPriority, getPriorityContext);
93
94
function markChildAsProgressed(current, workInProgress, priorityLevel) {
95
// We now have clones. Let's store them as the currently progressed work.
@@ -199,20 +200,20 @@ module.exports = function<T, P, I, TI, C, CX>(
200
return workInProgress.child;
201
}
202
202
- function updateClassComponent(current : ?Fiber, workInProgress : Fiber) {
203
+ function updateClassComponent(current : ?Fiber, workInProgress : Fiber, priorityLevel : PriorityLevel) {
204
let shouldUpdate;
205
if (!current) {
206
if (!workInProgress.stateNode) {
207
// In the initial pass we might need to construct the instance.
208
constructClassInstance(workInProgress);
208
- mountClassInstance(workInProgress);
209
+ mountClassInstance(workInProgress, priorityLevel);
210
shouldUpdate = true;
211
} else {
212
// In a resume, we'll already have an instance we can reuse.
212
- shouldUpdate = resumeMountClassInstance(workInProgress);
213
+ shouldUpdate = resumeMountClassInstance(workInProgress, priorityLevel);
214
}
215
} else {
215
- shouldUpdate = updateClassInstance(current, workInProgress);
216
+ shouldUpdate = updateClassInstance(current, workInProgress, priorityLevel);
217
}
218
219
// Schedule side-effects
@@ -305,7 +306,7 @@ module.exports = function<T, P, I, TI, C, CX>(
306
}
307
}
308
308
- function mountIndeterminateComponent(current, workInProgress) {
309
+ function mountIndeterminateComponent(current, workInProgress, priorityLevel) {
310
if (current) {
311
throw new Error('An indeterminate component should never have mounted.');
312
}
@@ -326,7 +327,7 @@ module.exports = function<T, P, I, TI, C, CX>(
327
// Proceed under the assumption that this is a class instance
328
workInProgress.tag = ClassComponent;
329
adoptClassInstance(workInProgress, value);
329
- mountClassInstance(workInProgress);
330
+ mountClassInstance(workInProgress, priorityLevel);
331
ReactCurrentOwner.current = workInProgress;
332
value = value.render();
333
} else {
@@ -509,11 +510,11 @@ module.exports = function<T, P, I, TI, C, CX>(
510
511
switch (workInProgress.tag) {
512
case IndeterminateComponent:
512
- return mountIndeterminateComponent(current, workInProgress);
513
+ return mountIndeterminateComponent(current, workInProgress, priorityLevel);
514
case FunctionalComponent:
515
return updateFunctionalComponent(current, workInProgress);
516
case ClassComponent:
516
- return updateClassComponent(current, workInProgress);
517
+ return updateClassComponent(current, workInProgress, priorityLevel);
518
case HostRoot: {
519
const root = (workInProgress.stateNode : FiberRoot);
520
if (root.pendingContext) {
@@ -526,9 +527,7 @@ module.exports = function<T, P, I, TI, C, CX>(
527
}
528
529
if (updateQueue) {
529
- // The last three arguments are unimportant because there should be
530
- // no update functions in a HostRoot's queue.
531
- beginUpdateQueue(workInProgress, updateQueue, null, null, null);
530
+ beginUpdateQueue(workInProgress, updateQueue, null, null, null, priorityLevel);
531
}
532
533
pushHostContainer(workInProgress.stateNode.containerInfo);
src/renderers/shared/fiber/ReactFiberClassComponent.js
+49
-31
@@ -13,13 +13,13 @@
13
'use strict';
14
15
import type { Fiber } from 'ReactFiber';
16
-import type { UpdateQueue } from 'ReactFiberUpdateQueue';
16
+import type { PriorityLevel } from 'ReactPriorityLevel';
17
18
var {
19
getMaskedContext,
20
} = require('ReactFiberContext');
21
var {
22
- createUpdateQueue,
22
+ ensureUpdateQueue,
23
addUpdate,
24
addReplaceUpdate,
25
addForceUpdate,
@@ -36,44 +36,41 @@ var invariant = require('invariant');
36
37
const isArray = Array.isArray;
38
39
-module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
40
-
41
- function scheduleUpdateQueue(fiber: Fiber, updateQueue: UpdateQueue) {
42
- fiber.updateQueue = updateQueue;
43
- // Schedule update on the alternate as well, since we don't know which tree
44
- // is current.
45
- if (fiber.alternate) {
46
- fiber.alternate.updateQueue = updateQueue;
47
- }
48
- scheduleUpdate(fiber);
49
- }
39
+module.exports = function(
40
+ scheduleUpdateAtPriority : (fiber: Fiber, priorityLevel : PriorityLevel) => void,
41
+ getPriorityContext : () => PriorityLevel,
42
+) {
43
44
// Class component state updater
45
const updater = {
46
isMounted,
47
enqueueSetState(instance, partialState) {
48
const fiber = ReactInstanceMap.get(instance);
56
- const queue = fiber.updateQueue || createUpdateQueue();
57
- addUpdate(queue, partialState);
58
- scheduleUpdateQueue(fiber, queue);
49
+ const queue = ensureUpdateQueue(fiber);
50
+ const priorityLevel = getPriorityContext();
51
+ addUpdate(queue, partialState, priorityLevel);
52
+ scheduleUpdateAtPriority(fiber, priorityLevel);
53
},
54
enqueueReplaceState(instance, state) {
55
const fiber = ReactInstanceMap.get(instance);
62
- const queue = fiber.updateQueue || createUpdateQueue();
63
- addReplaceUpdate(queue, state);
64
- scheduleUpdateQueue(fiber, queue);
56
+ const queue = ensureUpdateQueue(fiber);
57
+ const priorityLevel = getPriorityContext();
58
+ addReplaceUpdate(queue, state, priorityLevel);
59
+ scheduleUpdateAtPriority(fiber, priorityLevel);
60
},
61
enqueueForceUpdate(instance) {
62
const fiber = ReactInstanceMap.get(instance);
68
- const queue = fiber.updateQueue || createUpdateQueue();
69
- addForceUpdate(queue);
70
- scheduleUpdateQueue(fiber, queue);
63
+ const queue = ensureUpdateQueue(fiber);
64
+ const priorityLevel = getPriorityContext();
65
+ addForceUpdate(queue, priorityLevel);
66
+ scheduleUpdateAtPriority(fiber, priorityLevel);
67
},
68
enqueueCallback(instance, callback) {
69
const fiber = ReactInstanceMap.get(instance);
74
- const queue = fiber.updateQueue || createUpdateQueue();
75
- addCallback(queue, callback);
76
- scheduleUpdateQueue(fiber, queue);
70
+ const queue = ensureUpdateQueue(fiber);
71
+ const priorityLevel = getPriorityContext();
72
+ addCallback(queue, callback, priorityLevel);
73
+ scheduleUpdateAtPriority(fiber, priorityLevel);
74
},
75
};
76
@@ -227,7 +224,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
224
}
225
226
// Invokes the mount life-cycles on a previously never rendered instance.
230
- function mountClassInstance(workInProgress : Fiber) : void {
227
+ function mountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : void {
228
const instance = workInProgress.stateNode;
229
const state = instance.state || null;
230
@@ -246,14 +243,21 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
243
// process them now.
244
const updateQueue = workInProgress.updateQueue;
245
if (updateQueue) {
249
- instance.state = beginUpdateQueue(workInProgress, updateQueue, instance, state, props);
246
+ instance.state = beginUpdateQueue(
247
+ workInProgress,
248
+ updateQueue,
249
+ instance,
250
+ state,
251
+ props,
252
+ priorityLevel
253
+ );
254
}
255
}
256
}
257
258
// Called on a preexisting class instance. Returns false if a resumed render
259
// could be reused.
256
- function resumeMountClassInstance(workInProgress : Fiber) : boolean {
260
+ function resumeMountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
261
let newState = workInProgress.memoizedState;
262
let newProps = workInProgress.pendingProps;
263
if (!newProps) {
@@ -295,13 +299,20 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
299
// during initial mounting.
300
const newUpdateQueue = workInProgress.updateQueue;
301
if (newUpdateQueue) {
298
- newInstance.state = beginUpdateQueue(workInProgress, newUpdateQueue, newInstance, newState, newProps);
302
+ newInstance.state = beginUpdateQueue(
303
+ workInProgress,
304
+ newUpdateQueue,
305
+ newInstance,
306
+ newState,
307
+ newProps,
308
+ priorityLevel
309
+ );
310
}
311
return true;
312
}
313
314
// Invokes the update life-cycles and returns false if it shouldn't rerender.
304
- function updateClassInstance(current : Fiber, workInProgress : Fiber) : boolean {
315
+ function updateClassInstance(current : Fiber, workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
316
const instance = workInProgress.stateNode;
317
318
const oldProps = workInProgress.memoizedProps || current.memoizedProps;
@@ -333,7 +344,14 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
344
// TODO: Previous state can be null.
345
let newState;
346
if (updateQueue) {
336
- newState = beginUpdateQueue(workInProgress, updateQueue, instance, oldState, newProps);
347
+ newState = beginUpdateQueue(
348
+ workInProgress,
349
+ updateQueue,
350
+ instance,
351
+ oldState,
352
+ newProps,
353
+ priorityLevel
354
+ );
355
} else {
356
newState = oldState;
357
}
src/renderers/shared/fiber/ReactFiberReconciler.js
+9
-5
@@ -24,7 +24,7 @@ var {
24
var { createFiberRoot } = require('ReactFiberRoot');
25
var ReactFiberScheduler = require('ReactFiberScheduler');
26
27
-var { createUpdateQueue, addCallback } = require('ReactFiberUpdateQueue');
27
+var { ensureUpdateQueue, addCallback } = require('ReactFiberUpdateQueue');
28
29
if (__DEV__) {
30
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
@@ -99,6 +99,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
99
100
var {
101
scheduleWork,
102
+ getPriorityContext,
103
performWithPriority,
104
batchedUpdates,
105
syncUpdates,
@@ -114,13 +115,15 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
115
// TODO: Use the updateQueue and scheduleUpdate, instead of pendingProps.
116
// TODO: This should not override the pendingWorkPriority if there is
117
// higher priority work in the subtree.
118
+
119
current.pendingProps = element;
120
if (current.alternate) {
121
current.alternate.pendingProps = element;
122
}
123
if (callback) {
122
- const queue = current.updateQueue || createUpdateQueue();
123
- addCallback(queue, callback);
124
+ const queue = ensureUpdateQueue(current);
125
+ const priorityLevel = getPriorityContext();
126
+ addCallback(queue, callback, priorityLevel);
127
current.updateQueue = queue;
128
if (current.alternate) {
129
current.alternate.updateQueue = queue;
@@ -154,8 +157,9 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
157
current.alternate.pendingProps = element;
158
}
159
if (callback) {
157
- const queue = current.updateQueue || createUpdateQueue();
158
- addCallback(queue, callback);
160
+ const queue = ensureUpdateQueue(current);
161
+ const priorityLevel = getPriorityContext();
162
+ addCallback(queue, callback, priorityLevel);
163
current.updateQueue = queue;
164
if (current.alternate) {
165
current.alternate.updateQueue = queue;
src/renderers/shared/fiber/ReactFiberScheduler.js
+31
-14
@@ -54,6 +54,10 @@ var {
54
ClassComponent,
55
} = require('ReactTypeOfWork');
56
57
+var {
58
+ getPendingPriority,
59
+} = require('ReactFiberUpdateQueue');
60
+
61
var {
62
unwindContext,
63
} = require('ReactFiberContext');
@@ -69,7 +73,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
73
const hostContext = ReactFiberHostContext(config);
74
const { popHostContainer, popHostContext, resetHostContainer } = hostContext;
75
const { beginWork, beginFailedWork } =
72
- ReactFiberBeginWork(config, hostContext, scheduleUpdate);
76
+ ReactFiberBeginWork(config, hostContext, scheduleUpdateAtPriority, getPriorityContext);
77
const { completeWork } = ReactFiberCompleteWork(config, hostContext);
78
const {
79
commitPlacement,
@@ -86,6 +90,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
90
} = config;
91
92
// The priority level to use when scheduling an update.
93
+ // TODO: Should we change this to an array? Might be less confusing.
94
let priorityContext : PriorityLevel = useSyncScheduling ?
95
SynchronousPriority :
96
LowPriority;
@@ -140,6 +145,15 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
145
}
146
}
147
148
+ function getPriorityContext() : PriorityLevel {
149
+ let priorityLevel = priorityContext;
150
+ // If we're in a batch, downgrade sync priority to task priority
151
+ if (priorityLevel === SynchronousPriority && isPerformingWork) {
152
+ priorityLevel = TaskPriority;
153
+ }
154
+ return priorityLevel;
155
+ }
156
+
157
function findNextUnitOfWork() {
158
// Clear out roots with no more work on them, or if they have uncaught errors
159
while (nextScheduledRoot && nextScheduledRoot.current.pendingWorkPriority === NoWork) {
@@ -357,7 +371,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
371
// If we caught any errors during this commit, schedule their boundaries
372
// to update.
373
if (commitPhaseBoundaries) {
360
- commitPhaseBoundaries.forEach(scheduleUpdate);
374
+ commitPhaseBoundaries.forEach(scheduleErrorRecovery);
375
commitPhaseBoundaries = null;
376
}
377
@@ -366,6 +380,13 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
380
381
function resetWorkPriority(workInProgress : Fiber) {
382
let newPriority = NoWork;
383
+
384
+ // Check for pending update priority
385
+ const queue = workInProgress.updateQueue;
386
+ if (queue) {
387
+ newPriority = getPendingPriority(queue);
388
+ }
389
+
390
// progressedChild is going to be the child set with the highest priority.
391
// Either it is the same as child, or it just bailed out because it choose
392
// not to do the work.
@@ -829,11 +850,8 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
850
}
851
commitPhaseBoundaries.add(boundary);
852
} else {
832
- // Otherwise, schedule an update now. Error recovery has Task priority.
833
- const previousPriorityContext = priorityContext;
834
- priorityContext = TaskPriority;
835
- scheduleUpdate(boundary);
836
- priorityContext = previousPriorityContext;
853
+ // Otherwise, schedule an update now.
854
+ scheduleErrorRecovery(boundary);
855
}
856
return boundary;
857
} else if (!firstUncaughtError) {
@@ -983,13 +1001,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1001
}
1002
}
1003
986
- function scheduleUpdate(fiber : Fiber) {
987
- let priorityLevel = priorityContext;
988
- // If we're in a batch, downgrade sync priority to task priority
989
- if (priorityLevel === SynchronousPriority && isPerformingWork) {
990
- priorityLevel = TaskPriority;
991
- }
992
-
1004
+ function scheduleUpdateAtPriority(fiber : Fiber, priorityLevel : PriorityLevel) {
1005
let node = fiber;
1006
let shouldContinue = true;
1007
while (node && shouldContinue) {
@@ -1024,6 +1036,10 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1036
}
1037
}
1038
1039
+ function scheduleErrorRecovery(fiber : Fiber) {
1040
+ scheduleUpdateAtPriority(fiber, TaskPriority);
1041
+ }
1042
+
1043
function performWithPriority(priorityLevel : PriorityLevel, fn : Function) {
1044
const previousPriorityContext = priorityContext;
1045
priorityContext = priorityLevel;
@@ -1062,6 +1078,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
1078
1079
return {
1080
scheduleWork: scheduleWork,
1081
+ getPriorityContext: getPriorityContext,
1082
performWithPriority: performWithPriority,
1083
batchedUpdates: batchedUpdates,
1084
syncUpdates: syncUpdates,
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+83
-25
@@ -13,12 +13,15 @@
13
'use strict';
14
15
import type { Fiber } from 'ReactFiber';
16
+import type { PriorityLevel } from 'ReactPriorityLevel';
17
18
const {
19
ForceUpdate,
20
Callback: CallbackEffect,
21
} = require('ReactTypeOfSideEffect');
22
23
+const { NoWork } = require('ReactPriorityLevel');
24
+
25
type PartialState<State, Props> =
26
$Subtype<State> |
27
(prevState: State, props: Props) => $Subtype<State>;
@@ -26,6 +29,7 @@ type PartialState<State, Props> =
29
type Callback = () => void;
30
31
type Update = {
32
+ priorityLevel: PriorityLevel,
33
partialState: PartialState<any, any>,
34
callback: Callback | null,
35
isReplace: boolean,
@@ -44,19 +48,31 @@ export type UpdateQueue = {
48
last: Update | null,
49
};
50
47
-exports.createUpdateQueue = function() : UpdateQueue {
48
- return {
51
+// Ensures that a fiber and its alternate have an update queue, creating a newText
52
+// one if needed. Returns the new or existing queue.
53
+function ensureUpdateQueue(fiber : Fiber) : UpdateQueue {
54
+ if (fiber.updateQueue) {
55
+ // We already have an update queue.
56
+ return fiber.updateQueue;
57
+ }
58
+ const queue = {
59
first: null,
60
firstPendingUpdate: null,
61
last: null,
62
};
53
-};
63
+ fiber.updateQueue = queue;
64
+ // Add queue to the alternate as well, because when we call setState we don't
65
+ // know which tree is current.
66
+ if (fiber.alternate) {
67
+ fiber.alternate.updateQueue = queue;
68
+ }
69
+ return queue;
70
+}
71
+exports.ensureUpdateQueue = ensureUpdateQueue;
72
55
-function insertUpdateIntoQueue(queue : UpdateQueue, update : Update) : void {
73
+function insertUpdateIntoQueue(queue : UpdateQueue, update : Update, priorityLevel : PriorityLevel) : void {
74
// Add a pending update to the end of the queue.
57
- // TODO: Once updates have priorities, they should be inserted in the
58
- // correct order. Addtionally, replaceState should remove any pending updates
59
- // that have lower priority from queue.
75
+ // TODO: Insert updates in order of priority.
76
if (!queue.last) {
77
// The queue is empty.
78
queue.first = queue.last = queue.firstPendingUpdate = update;
@@ -72,19 +88,30 @@ function insertUpdateIntoQueue(queue : UpdateQueue, update : Update) : void {
88
}
89
}
90
75
-exports.addUpdate = function(queue : UpdateQueue, partialState : PartialState<any, any> | null) : void {
91
+function addUpdate(
92
+ queue : UpdateQueue,
93
+ partialState : PartialState<any, any> | null,
94
+ priorityLevel : PriorityLevel
95
+) : void {
96
const update = {
97
+ priorityLevel,
98
partialState,
99
callback: null,
100
isReplace: false,
101
isForced: false,
102
next: null,
103
};
83
- insertUpdateIntoQueue(queue, update);
84
-};
104
+ insertUpdateIntoQueue(queue, update, priorityLevel);
105
+}
106
+exports.addUpdate = addUpdate;
107
86
-exports.addReplaceUpdate = function(queue : UpdateQueue, state : any | null) : void {
108
+function addReplaceUpdate(
109
+ queue : UpdateQueue,
110
+ state : any | null,
111
+ priorityLevel : PriorityLevel
112
+) : void {
113
const replaceUpdate = {
114
+ priorityLevel,
115
partialState: state,
116
callback: null,
117
isReplace: true,
@@ -92,7 +119,6 @@ exports.addReplaceUpdate = function(queue : UpdateQueue, state : any | null) : v
119
next: null,
120
};
121
95
-
122
if (!queue.last) {
123
// The queue is empty.
124
queue.first = queue.last = queue.firstPendingUpdate = replaceUpdate;
@@ -124,21 +150,24 @@ exports.addReplaceUpdate = function(queue : UpdateQueue, state : any | null) : v
150
}
151
}
152
127
-};
153
+}
154
+exports.addReplaceUpdate = addReplaceUpdate;
155
129
-exports.addForceUpdate = function(queue : UpdateQueue) : void {
156
+function addForceUpdate(queue : UpdateQueue, priorityLevel : PriorityLevel) : void {
157
const update = {
158
+ priorityLevel,
159
partialState: null,
160
callback: null,
161
isReplace: false,
162
isForced: true,
163
next: null,
164
};
137
- insertUpdateIntoQueue(queue, update);
138
-};
165
+ insertUpdateIntoQueue(queue, update, priorityLevel);
166
+}
167
+exports.addForceUpdate = addForceUpdate;
168
169
141
-exports.addCallback = function(queue : UpdateQueue, callback: Callback) : void {
170
+function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : PriorityLevel) : void {
171
if (queue.firstPendingUpdate && queue.last && !queue.last.callback) {
172
// If pending updates already exist, and the last pending update does not
173
// have a callback, we can add the new callback to that update.
@@ -148,19 +177,39 @@ exports.addCallback = function(queue : UpdateQueue, callback: Callback) : void {
177
}
178
179
const update = {
180
+ priorityLevel,
181
partialState: null,
182
callback,
183
isReplace: false,
184
isForced: false,
185
next: null,
186
};
157
- insertUpdateIntoQueue(queue, update);
158
-};
187
+ insertUpdateIntoQueue(queue, update, priorityLevel);
188
+}
189
+exports.addCallback = addCallback;
190
160
-exports.hasPendingUpdate = function(queue : UpdateQueue) : boolean {
191
+function hasPendingUpdate(queue : UpdateQueue) : boolean {
192
// TODO: Check priority level
193
return queue.firstPendingUpdate !== null;
163
-};
194
+}
195
+exports.hasPendingUpdate = hasPendingUpdate;
196
+
197
+function getPendingPriority(queue : UpdateQueue) : PriorityLevel {
198
+ // Loop through the pending updates to recompute the pending priority.
199
+ // TODO: Once updates are sorted, just read from the first pending update.
200
+ let priorityLevel = NoWork;
201
+ let update = queue.firstPendingUpdate;
202
+ while (update) {
203
+ if (priorityLevel === NoWork ||
204
+ priorityLevel >= update.priorityLevel) {
205
+ // Update pending priority
206
+ priorityLevel = update.priorityLevel;
207
+ }
208
+ update = update.next;
209
+ }
210
+ return priorityLevel;
211
+}
212
+exports.getPendingPriority = getPendingPriority;
213
214
function getStateFromUpdate(update, instance, prevState, props) {
215
const partialState = update.partialState;
@@ -172,7 +221,14 @@ function getStateFromUpdate(update, instance, prevState, props) {
221
}
222
}
223
175
-exports.beginUpdateQueue = function(workInProgress : Fiber, queue : UpdateQueue, instance : any, prevState : any, props : any) : any {
224
+function beginUpdateQueue(
225
+ workInProgress : Fiber,
226
+ queue : UpdateQueue,
227
+ instance : any,
228
+ prevState : any,
229
+ props : any,
230
+ priorityLevel : PriorityLevel
231
+) : any {
232
// This merges the entire update queue into a single object, not just the
233
// pending updates, because the previous state and props may have changed.
234
// TODO: Would memoization be worth it?
@@ -227,9 +283,10 @@ exports.beginUpdateQueue = function(workInProgress : Fiber, queue : UpdateQueue,
283
}
284
285
return state;
230
-};
286
+}
287
+exports.beginUpdateQueue = beginUpdateQueue;
288
232
-exports.commitUpdateQueue = function(finishedWork : Fiber, queue : UpdateQueue, context : mixed) {
289
+function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context : mixed) {
290
if (finishedWork.effectTag & CallbackEffect) {
291
// Call the callbacks on all the non-pending updates.
292
let update = queue.first;
@@ -254,4 +311,5 @@ exports.commitUpdateQueue = function(finishedWork : Fiber, queue : UpdateQueue,
311
finishedWork.alternate.updateQueue = null;
312
}
313
}
257
-};
314
+}
315
+exports.commitUpdateQueue = commitUpdateQueue;