@samitouri / QOS-React-2 / commits / 94011e98a6

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;