diff --git a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js index 544db31edb..721ff0259c 100644 --- a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js +++ b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js @@ -162,6 +162,26 @@ function insertUpdateIntoQueue(queue, update, insertAfter, insertBefore) { } } +// Returns the update after which the incoming update should be inserted into +// the queue, or null if it should be inserted at beginning. +function findInsertionPosition(queue, update) : Update | null { + const priorityLevel = update.priorityLevel; + let insertAfter = null; + let insertBefore = null; + if (queue.last && comparePriority(queue.last.priorityLevel, priorityLevel) <= 0) { + // Fast path for the common case where the update should be inserted at + // the end of the queue. + insertAfter = queue.last; + } else { + insertBefore = queue.first; + while (insertBefore && comparePriority(insertBefore.priorityLevel, priorityLevel) <= 0) { + insertAfter = insertBefore; + insertBefore = insertBefore.next; + } + } + return insertAfter; +} + // The work-in-progress queue is a subset of the current queue (if it exists). // We need to insert the incoming update into both lists. However, it's possible // that the correct position in one list will be different from the position in @@ -189,7 +209,6 @@ function insertUpdateIntoQueue(queue, update, insertAfter, insertBefore) { // // However, if incoming update is inserted into the same position of both lists, // we shouldn't make a copy. - function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : void { const queue1 = ensureUpdateQueue(fiber); const queue2 = fiber.alternate ? ensureUpdateQueue(fiber.alternate) : null; @@ -218,54 +237,40 @@ function insertUpdate(fiber : Fiber, update : Update, methodName : ?string) : vo } } - const priorityLevel = update.priorityLevel; + // Find the insertion position in the first queue. + const insertAfter1 = findInsertionPosition(queue1, update); + const insertBefore1 = insertAfter1 ? insertAfter1.next : queue1.first; - let queue = queue1; - let insertAfter1; - let insertBefore1; - let insertAfter2; - let insertBefore2; - for (let i = 0; queue && i < 2; i++) { - let insertAfter = null; - let insertBefore = null; - if (queue.last && comparePriority(queue.last.priorityLevel, priorityLevel) <= 0) { - // Fast path for the common case where the update should be inserted at - // the end of the queue. - insertAfter = queue.last; - } else { - insertBefore = queue.first; - while (insertBefore && comparePriority(insertBefore.priorityLevel, priorityLevel) <= 0) { - insertAfter = insertBefore; - insertBefore = insertBefore.next; - } - } - if (i === 0) { - insertAfter1 = insertAfter; - insertBefore1 = insertBefore; - queue = queue2; - } else { - insertAfter2 = insertAfter; - insertBefore2 = insertBefore; - queue = null; - } + if (!queue2) { + // If there's no alternate queue, there's nothing else to do but insert. + insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1); + return; } - const update1 = update; - insertUpdateIntoQueue(queue1, update1, insertAfter1, insertBefore1); + // If there is an alternate queue, find the insertion position. + const insertAfter2 = findInsertionPosition(queue2, update); + const insertBefore2 = insertAfter2 ? insertAfter2.next : queue2.first; - if (queue2) { - let update2; - if (insertBefore1 === insertBefore2) { - // The update is inserted into the same position of both lists. There's no - // need to clone the update. - update2 = update1; - } else { - // The update is inserted into two separate positions. Make a clone of the - // update and insert it twice. One or the other will be dropped the next - // time we commit. - update2 = cloneUpdate(update1); - } + // Now we can insert into the first queue. This must come after finding both + // insertion positions because it mutates the list. + insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1); + + if (insertBefore1 !== insertBefore2) { + // The insertion positions are different, so we need to clone the update and + // insert the clone into the alternate queue. + const update2 = cloneUpdate(update); insertUpdateIntoQueue(queue2, update2, insertAfter2, insertBefore2); + } else { + // The insertion positions are the same, so when we inserted into the first + // queue, it also inserted into the alternate. All we need to do is update + // the alternate queue's `first` and `last` pointers, in case they + // have changed. + if (!insertAfter2) { + queue2.first = update; + } + if (!insertBefore2) { + queue2.last = null; + } } }