Use lastProgressedUpdate pointer instead of firstPendingUpdate
We need to be able to access both, and since the list uses forward pointers, it makes more sense to point to the one that comes first. Otherwise to get the last progressed update you have to start at the beginning of the list.
Andrew Clark committed
Dec 10, 2016 at 02:24 UTC
bb844a03b2c1c1910baefeb2e24bc29d6095afd5
3 files changed
+53
-48
src/renderers/noop/ReactNoop.js
+7
-1
@@ -286,10 +286,16 @@ var ReactNoop = {
286
log(
287
' '.repeat(depth + 1) + 'QUEUED UPDATES'
288
);
289
- const firstPendingUpdate = updateQueue.firstPendingUpdate;
289
+ let firstPendingUpdate;
290
+ if (updateQueue.lastProgressedUpdate) {
291
+ firstPendingUpdate = updateQueue.lastProgressedUpdate.next;
292
+ } else {
293
+ firstPendingUpdate = updateQueue.first;
294
+ }
295
if (!firstPendingUpdate) {
296
return;
297
}
298
+
299
log(
300
' '.repeat(depth + 1) + '~',
301
firstPendingUpdate && firstPendingUpdate.partialState,
src/renderers/shared/fiber/ReactFiberBeginWork.js
+1
-1
@@ -502,7 +502,7 @@ module.exports = function<T, P, I, TI, C, CX>(
502
) // )
503
);
504
if (!hasNewProps) {
505
- const hasUpdate = updateQueue && hasPendingUpdate(updateQueue);
505
+ const hasUpdate = updateQueue && hasPendingUpdate(updateQueue, priorityLevel);
506
if (!hasUpdate && !hasContextChanged()) {
507
return bailoutOnAlreadyFinishedWork(current, workInProgress);
508
}
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+45
-46
@@ -38,17 +38,34 @@ type Update = {
38
};
39
40
export type UpdateQueue = {
41
- // Points to the first (oldest) update.
41
+ // Points to the first update.
42
first: Update | null,
43
- // Points to the first pending update. A pending update is one that is not
44
- // part of the progressed work. This could be null even in a non-empty queue,
45
- // when none of the updates are empty.
46
- firstPendingUpdate: Update | null,
47
- // Points to the last (newest) update.
43
+ lastProgressedUpdate: Update | null,
44
+ // Points to the last update.
45
last: Update | null,
46
};
47
51
-// Ensures that a fiber and its alternate have an update queue, creating a newText
48
+function getFirstPendingUpdate(queue : UpdateQueue) {
49
+ if (queue.lastProgressedUpdate) {
50
+ return queue.lastProgressedUpdate.next;
51
+ }
52
+ return queue.first;
53
+}
54
+
55
+function getFirstProgressedUpdate(queue : UpdateQueue) {
56
+ if (queue.lastProgressedUpdate) {
57
+ return queue.first;
58
+ }
59
+ return null;
60
+}
61
+
62
+function hasPendingUpdate(queue : UpdateQueue, priorityLevel : PriorityLevel) : boolean {
63
+ // TODO: Check priority level
64
+ return Boolean(getFirstPendingUpdate(queue));
65
+}
66
+exports.hasPendingUpdate = hasPendingUpdate;
67
+
68
+// Ensures that a fiber and its alternate have an update queue, creating a new
69
// one if needed. Returns the new or existing queue.
70
function ensureUpdateQueue(fiber : Fiber) : UpdateQueue {
71
if (fiber.updateQueue) {
@@ -57,7 +74,7 @@ function ensureUpdateQueue(fiber : Fiber) : UpdateQueue {
74
}
75
const queue = {
76
first: null,
60
- firstPendingUpdate: null,
77
+ lastProgressedUpdate: null,
78
last: null,
79
};
80
fiber.updateQueue = queue;
@@ -75,15 +92,13 @@ function insertUpdateIntoQueue(queue : UpdateQueue, update : Update, priorityLev
92
// TODO: Insert updates in order of priority.
93
if (!queue.last) {
94
// The queue is empty.
78
- queue.first = queue.last = queue.firstPendingUpdate = update;
95
+ queue.first = queue.last = update;
96
} else {
97
// The queue is not empty. Append the update to the end.
98
queue.last.next = update;
99
queue.last = update;
83
-
84
- if (!queue.firstPendingUpdate) {
85
- // This is the first pending update. Update the pointer.
86
- queue.firstPendingUpdate = update;
100
+ if (queue.lastProgressedUpdate && !queue.lastProgressedUpdate.next) {
101
+ queue.lastProgressedUpdate.next = update;
102
}
103
}
104
}
@@ -121,32 +136,18 @@ function addReplaceUpdate(
136
137
if (!queue.last) {
138
// The queue is empty.
124
- queue.first = queue.last = queue.firstPendingUpdate = replaceUpdate;
139
+ queue.first = queue.last = replaceUpdate;
140
} else {
141
// The queue is not empty.
142
143
// Drop all existing pending updates.
144
// TODO: Only drop updates with matching priority.
130
- let lastMergedUpdate = null;
131
- if (queue.firstPendingUpdate) {
132
- let node = queue.first;
133
- while (node && node.next !== queue.firstPendingUpdate) {
134
- node = node.next;
135
- }
136
- lastMergedUpdate = node;
137
- } else {
138
- lastMergedUpdate = queue.last;
139
- }
140
-
141
- if (lastMergedUpdate) {
142
- // Append the new update to the end of the list.
143
- // $FlowFixMe: Union bug (I think? Getting "object literal - This type incompatible with null")
144
- lastMergedUpdate.next = replaceUpdate;
145
- queue.firstPendingUpdate = replaceUpdate;
145
+ if (queue.lastProgressedUpdate) {
146
+ queue.lastProgressedUpdate.next = replaceUpdate;
147
queue.last = replaceUpdate;
148
} else {
149
// Drop everything
149
- queue.first = queue.firstPendingUpdate = queue.last = replaceUpdate;
150
+ queue.first = queue.last = replaceUpdate;
151
}
152
}
153
@@ -168,7 +169,7 @@ exports.addForceUpdate = addForceUpdate;
169
170
171
function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : PriorityLevel) : void {
171
- if (queue.firstPendingUpdate && queue.last && !queue.last.callback) {
172
+ if (getFirstPendingUpdate(queue) && queue.last && !queue.last.callback) {
173
// If pending updates already exist, and the last pending update does not
174
// have a callback, we can add the new callback to that update.
175
// TODO: Add an additional check to ensure the priority matches.
@@ -188,17 +189,12 @@ function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : Pr
189
}
190
exports.addCallback = addCallback;
191
191
-function hasPendingUpdate(queue : UpdateQueue) : boolean {
192
- // TODO: Check priority level
193
- return queue.firstPendingUpdate !== null;
194
-}
195
-exports.hasPendingUpdate = hasPendingUpdate;
196
-
192
function getPendingPriority(queue : UpdateQueue) : PriorityLevel {
193
// Loop through the pending updates to recompute the pending priority.
194
// TODO: Once updates are sorted, just read from the first pending update.
195
let priorityLevel = NoWork;
201
- let update = queue.firstPendingUpdate;
196
+ // Start with first pending update
197
+ let update = getFirstPendingUpdate(queue);
198
while (update) {
199
if (priorityLevel === NoWork ||
200
priorityLevel >= update.priorityLevel) {
@@ -239,11 +235,12 @@ function beginUpdateQueue(
235
236
let state = prevState;
237
let dontMutatePrevState = true;
242
- let update : Update | null = queue.first;
238
let isEmpty = true;
239
240
// TODO: Stop merging once we reach an update whose priority doesn't match.
241
// Should this also apply to updates that were previous merged but bailed out?
242
+ let update : Update | null = queue.first;
243
+ let lastProgressedUpdate = null;
244
while (update) {
245
let partialState;
246
if (update.isReplace) {
@@ -270,12 +267,11 @@ function beginUpdateQueue(
267
if (update.callback) {
268
workInProgress.effectTag |= CallbackEffect;
269
}
270
+ lastProgressedUpdate = update;
271
update = update.next;
272
}
273
276
- // The next pending update is the one that we exited on in the loop above.
277
- // Until priorities are implemented, this is always null.
278
- queue.firstPendingUpdate = update;
274
+ queue.lastProgressedUpdate = lastProgressedUpdate;
275
276
if (isEmpty) {
277
// None of the updates contained state. Return the original state object.
@@ -287,10 +283,11 @@ function beginUpdateQueue(
283
exports.beginUpdateQueue = beginUpdateQueue;
284
285
function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context : mixed) {
286
+
287
if (finishedWork.effectTag & CallbackEffect) {
288
// Call the callbacks on all the non-pending updates.
292
- let update = queue.first;
293
- while (update && update !== queue.firstPendingUpdate) {
289
+ let update = getFirstProgressedUpdate(queue);
290
+ while (update && update !== getFirstPendingUpdate(queue)) {
291
const callback = update.callback;
292
if (typeof callback === 'function') {
293
callback.call(context);
@@ -300,8 +297,10 @@ function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context :
297
}
298
299
// Drop all completed updates, leaving only the pending updates.
303
- queue.first = queue.firstPendingUpdate;
300
+ queue.first = getFirstPendingUpdate(queue);
301
if (!queue.first) {
302
+ queue.last = queue.lastProgressedUpdate = null;
303
+
304
// If the list is now empty, we can remove it from the finished work
305
finishedWork.updateQueue = null;
306
if (finishedWork.alternate) {