1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. // UpdateQueue is a linked list of prioritized updates.
    
  11. //
    
  12. // Like fibers, update queues come in pairs: a current queue, which represents
    
  13. // the visible state of the screen, and a work-in-progress queue, which can be
    
  14. // mutated and processed asynchronously before it is committed — a form of
    
  15. // double buffering. If a work-in-progress render is discarded before finishing,
    
  16. // we create a new work-in-progress by cloning the current queue.
    
  17. //
    
  18. // Both queues share a persistent, singly-linked list structure. To schedule an
    
  19. // update, we append it to the end of both queues. Each queue maintains a
    
  20. // pointer to first update in the persistent list that hasn't been processed.
    
  21. // The work-in-progress pointer always has a position equal to or greater than
    
  22. // the current queue, since we always work on that one. The current queue's
    
  23. // pointer is only updated during the commit phase, when we swap in the
    
  24. // work-in-progress.
    
  25. //
    
  26. // For example:
    
  27. //
    
  28. //   Current pointer:           A - B - C - D - E - F
    
  29. //   Work-in-progress pointer:              D - E - F
    
  30. //                                          ^
    
  31. //                                          The work-in-progress queue has
    
  32. //                                          processed more updates than current.
    
  33. //
    
  34. // The reason we append to both queues is because otherwise we might drop
    
  35. // updates without ever processing them. For example, if we only add updates to
    
  36. // the work-in-progress queue, some updates could be lost whenever a work-in
    
  37. // -progress render restarts by cloning from current. Similarly, if we only add
    
  38. // updates to the current queue, the updates will be lost whenever an already
    
  39. // in-progress queue commits and swaps with the current queue. However, by
    
  40. // adding to both queues, we guarantee that the update will be part of the next
    
  41. // work-in-progress. (And because the work-in-progress queue becomes the
    
  42. // current queue once it commits, there's no danger of applying the same
    
  43. // update twice.)
    
  44. //
    
  45. // Prioritization
    
  46. // --------------
    
  47. //
    
  48. // Updates are not sorted by priority, but by insertion; new updates are always
    
  49. // appended to the end of the list.
    
  50. //
    
  51. // The priority is still important, though. When processing the update queue
    
  52. // during the render phase, only the updates with sufficient priority are
    
  53. // included in the result. If we skip an update because it has insufficient
    
  54. // priority, it remains in the queue to be processed later, during a lower
    
  55. // priority render. Crucially, all updates subsequent to a skipped update also
    
  56. // remain in the queue *regardless of their priority*. That means high priority
    
  57. // updates are sometimes processed twice, at two separate priorities. We also
    
  58. // keep track of a base state, that represents the state before the first
    
  59. // update in the queue is applied.
    
  60. //
    
  61. // For example:
    
  62. //
    
  63. //   Given a base state of '', and the following queue of updates
    
  64. //
    
  65. //     A1 - B2 - C1 - D2
    
  66. //
    
  67. //   where the number indicates the priority, and the update is applied to the
    
  68. //   previous state by appending a letter, React will process these updates as
    
  69. //   two separate renders, one per distinct priority level:
    
  70. //
    
  71. //   First render, at priority 1:
    
  72. //     Base state: ''
    
  73. //     Updates: [A1, C1]
    
  74. //     Result state: 'AC'
    
  75. //
    
  76. //   Second render, at priority 2:
    
  77. //     Base state: 'A'            <-  The base state does not include C1,
    
  78. //                                    because B2 was skipped.
    
  79. //     Updates: [B2, C1, D2]      <-  C1 was rebased on top of B2
    
  80. //     Result state: 'ABCD'
    
  81. //
    
  82. // Because we process updates in insertion order, and rebase high priority
    
  83. // updates when preceding updates are skipped, the final result is deterministic
    
  84. // regardless of priority. Intermediate state may vary according to system
    
  85. // resources, but the final state is always the same.
    
  86. 
    
  87. import type {Fiber, FiberRoot} from './ReactInternalTypes';
    
  88. import type {Lanes, Lane} from './ReactFiberLane';
    
  89. 
    
  90. import {
    
  91.   NoLane,
    
  92.   NoLanes,
    
  93.   OffscreenLane,
    
  94.   isSubsetOfLanes,
    
  95.   mergeLanes,
    
  96.   removeLanes,
    
  97.   isTransitionLane,
    
  98.   intersectLanes,
    
  99.   markRootEntangled,
    
  100. } from './ReactFiberLane';
    
  101. import {
    
  102.   enterDisallowedContextReadInDEV,
    
  103.   exitDisallowedContextReadInDEV,
    
  104. } from './ReactFiberNewContext';
    
  105. import {
    
  106.   Callback,
    
  107.   Visibility,
    
  108.   ShouldCapture,
    
  109.   DidCapture,
    
  110. } from './ReactFiberFlags';
    
  111. import getComponentNameFromFiber from './getComponentNameFromFiber';
    
  112. 
    
  113. import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags';
    
  114. 
    
  115. import {StrictLegacyMode} from './ReactTypeOfMode';
    
  116. import {
    
  117.   markSkippedUpdateLanes,
    
  118.   isUnsafeClassRenderPhaseUpdate,
    
  119.   getWorkInProgressRootRenderLanes,
    
  120. } from './ReactFiberWorkLoop';
    
  121. import {
    
  122.   enqueueConcurrentClassUpdate,
    
  123.   unsafe_markUpdateLaneFromFiberToRoot,
    
  124. } from './ReactFiberConcurrentUpdates';
    
  125. import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook';
    
  126. 
    
  127. import assign from 'shared/assign';
    
  128. 
    
  129. export type Update<State> = {
    
  130.   lane: Lane,
    
  131. 
    
  132.   tag: 0 | 1 | 2 | 3,
    
  133.   payload: any,
    
  134.   callback: (() => mixed) | null,
    
  135. 
    
  136.   next: Update<State> | null,
    
  137. };
    
  138. 
    
  139. export type SharedQueue<State> = {
    
  140.   pending: Update<State> | null,
    
  141.   lanes: Lanes,
    
  142.   hiddenCallbacks: Array<() => mixed> | null,
    
  143. };
    
  144. 
    
  145. export type UpdateQueue<State> = {
    
  146.   baseState: State,
    
  147.   firstBaseUpdate: Update<State> | null,
    
  148.   lastBaseUpdate: Update<State> | null,
    
  149.   shared: SharedQueue<State>,
    
  150.   callbacks: Array<() => mixed> | null,
    
  151. };
    
  152. 
    
  153. export const UpdateState = 0;
    
  154. export const ReplaceState = 1;
    
  155. export const ForceUpdate = 2;
    
  156. export const CaptureUpdate = 3;
    
  157. 
    
  158. // Global state that is reset at the beginning of calling `processUpdateQueue`.
    
  159. // It should only be read right after calling `processUpdateQueue`, via
    
  160. // `checkHasForceUpdateAfterProcessing`.
    
  161. let hasForceUpdate = false;
    
  162. 
    
  163. let didWarnUpdateInsideUpdate;
    
  164. let currentlyProcessingQueue: ?SharedQueue<$FlowFixMe>;
    
  165. export let resetCurrentlyProcessingQueue: () => void;
    
  166. if (__DEV__) {
    
  167.   didWarnUpdateInsideUpdate = false;
    
  168.   currentlyProcessingQueue = null;
    
  169.   resetCurrentlyProcessingQueue = () => {
    
  170.     currentlyProcessingQueue = null;
    
  171.   };
    
  172. }
    
  173. 
    
  174. export function initializeUpdateQueue<State>(fiber: Fiber): void {
    
  175.   const queue: UpdateQueue<State> = {
    
  176.     baseState: fiber.memoizedState,
    
  177.     firstBaseUpdate: null,
    
  178.     lastBaseUpdate: null,
    
  179.     shared: {
    
  180.       pending: null,
    
  181.       lanes: NoLanes,
    
  182.       hiddenCallbacks: null,
    
  183.     },
    
  184.     callbacks: null,
    
  185.   };
    
  186.   fiber.updateQueue = queue;
    
  187. }
    
  188. 
    
  189. export function cloneUpdateQueue<State>(
    
  190.   current: Fiber,
    
  191.   workInProgress: Fiber,
    
  192. ): void {
    
  193.   // Clone the update queue from current. Unless it's already a clone.
    
  194.   const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
    
  195.   const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
    
  196.   if (queue === currentQueue) {
    
  197.     const clone: UpdateQueue<State> = {
    
  198.       baseState: currentQueue.baseState,
    
  199.       firstBaseUpdate: currentQueue.firstBaseUpdate,
    
  200.       lastBaseUpdate: currentQueue.lastBaseUpdate,
    
  201.       shared: currentQueue.shared,
    
  202.       callbacks: null,
    
  203.     };
    
  204.     workInProgress.updateQueue = clone;
    
  205.   }
    
  206. }
    
  207. 
    
  208. export function createUpdate(lane: Lane): Update<mixed> {
    
  209.   const update: Update<mixed> = {
    
  210.     lane,
    
  211. 
    
  212.     tag: UpdateState,
    
  213.     payload: null,
    
  214.     callback: null,
    
  215. 
    
  216.     next: null,
    
  217.   };
    
  218.   return update;
    
  219. }
    
  220. 
    
  221. export function enqueueUpdate<State>(
    
  222.   fiber: Fiber,
    
  223.   update: Update<State>,
    
  224.   lane: Lane,
    
  225. ): FiberRoot | null {
    
  226.   const updateQueue = fiber.updateQueue;
    
  227.   if (updateQueue === null) {
    
  228.     // Only occurs if the fiber has been unmounted.
    
  229.     return null;
    
  230.   }
    
  231. 
    
  232.   const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
    
  233. 
    
  234.   if (__DEV__) {
    
  235.     if (
    
  236.       currentlyProcessingQueue === sharedQueue &&
    
  237.       !didWarnUpdateInsideUpdate
    
  238.     ) {
    
  239.       const componentName = getComponentNameFromFiber(fiber);
    
  240.       console.error(
    
  241.         'An update (setState, replaceState, or forceUpdate) was scheduled ' +
    
  242.           'from inside an update function. Update functions should be pure, ' +
    
  243.           'with zero side-effects. Consider using componentDidUpdate or a ' +
    
  244.           'callback.\n\nPlease update the following component: %s',
    
  245.         componentName,
    
  246.       );
    
  247.       didWarnUpdateInsideUpdate = true;
    
  248.     }
    
  249.   }
    
  250. 
    
  251.   if (isUnsafeClassRenderPhaseUpdate(fiber)) {
    
  252.     // This is an unsafe render phase update. Add directly to the update
    
  253.     // queue so we can process it immediately during the current render.
    
  254.     const pending = sharedQueue.pending;
    
  255.     if (pending === null) {
    
  256.       // This is the first update. Create a circular list.
    
  257.       update.next = update;
    
  258.     } else {
    
  259.       update.next = pending.next;
    
  260.       pending.next = update;
    
  261.     }
    
  262.     sharedQueue.pending = update;
    
  263. 
    
  264.     // Update the childLanes even though we're most likely already rendering
    
  265.     // this fiber. This is for backwards compatibility in the case where you
    
  266.     // update a different component during render phase than the one that is
    
  267.     // currently renderings (a pattern that is accompanied by a warning).
    
  268.     return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
    
  269.   } else {
    
  270.     return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
    
  271.   }
    
  272. }
    
  273. 
    
  274. export function entangleTransitions(root: FiberRoot, fiber: Fiber, lane: Lane) {
    
  275.   const updateQueue = fiber.updateQueue;
    
  276.   if (updateQueue === null) {
    
  277.     // Only occurs if the fiber has been unmounted.
    
  278.     return;
    
  279.   }
    
  280. 
    
  281.   const sharedQueue: SharedQueue<mixed> = (updateQueue: any).shared;
    
  282.   if (isTransitionLane(lane)) {
    
  283.     let queueLanes = sharedQueue.lanes;
    
  284. 
    
  285.     // If any entangled lanes are no longer pending on the root, then they must
    
  286.     // have finished. We can remove them from the shared queue, which represents
    
  287.     // a superset of the actually pending lanes. In some cases we may entangle
    
  288.     // more than we need to, but that's OK. In fact it's worse if we *don't*
    
  289.     // entangle when we should.
    
  290.     queueLanes = intersectLanes(queueLanes, root.pendingLanes);
    
  291. 
    
  292.     // Entangle the new transition lane with the other transition lanes.
    
  293.     const newQueueLanes = mergeLanes(queueLanes, lane);
    
  294.     sharedQueue.lanes = newQueueLanes;
    
  295.     // Even if queue.lanes already include lane, we don't know for certain if
    
  296.     // the lane finished since the last time we entangled it. So we need to
    
  297.     // entangle it again, just to be sure.
    
  298.     markRootEntangled(root, newQueueLanes);
    
  299.   }
    
  300. }
    
  301. 
    
  302. export function enqueueCapturedUpdate<State>(
    
  303.   workInProgress: Fiber,
    
  304.   capturedUpdate: Update<State>,
    
  305. ) {
    
  306.   // Captured updates are updates that are thrown by a child during the render
    
  307.   // phase. They should be discarded if the render is aborted. Therefore,
    
  308.   // we should only put them on the work-in-progress queue, not the current one.
    
  309.   let queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
    
  310. 
    
  311.   // Check if the work-in-progress queue is a clone.
    
  312.   const current = workInProgress.alternate;
    
  313.   if (current !== null) {
    
  314.     const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
    
  315.     if (queue === currentQueue) {
    
  316.       // The work-in-progress queue is the same as current. This happens when
    
  317.       // we bail out on a parent fiber that then captures an error thrown by
    
  318.       // a child. Since we want to append the update only to the work-in
    
  319.       // -progress queue, we need to clone the updates. We usually clone during
    
  320.       // processUpdateQueue, but that didn't happen in this case because we
    
  321.       // skipped over the parent when we bailed out.
    
  322.       let newFirst = null;
    
  323.       let newLast = null;
    
  324.       const firstBaseUpdate = queue.firstBaseUpdate;
    
  325.       if (firstBaseUpdate !== null) {
    
  326.         // Loop through the updates and clone them.
    
  327.         let update: Update<State> = firstBaseUpdate;
    
  328.         do {
    
  329.           const clone: Update<State> = {
    
  330.             lane: update.lane,
    
  331. 
    
  332.             tag: update.tag,
    
  333.             payload: update.payload,
    
  334.             // When this update is rebased, we should not fire its
    
  335.             // callback again.
    
  336.             callback: null,
    
  337. 
    
  338.             next: null,
    
  339.           };
    
  340.           if (newLast === null) {
    
  341.             newFirst = newLast = clone;
    
  342.           } else {
    
  343.             newLast.next = clone;
    
  344.             newLast = clone;
    
  345.           }
    
  346.           // $FlowFixMe[incompatible-type] we bail out when we get a null
    
  347.           update = update.next;
    
  348.         } while (update !== null);
    
  349. 
    
  350.         // Append the captured update the end of the cloned list.
    
  351.         if (newLast === null) {
    
  352.           newFirst = newLast = capturedUpdate;
    
  353.         } else {
    
  354.           newLast.next = capturedUpdate;
    
  355.           newLast = capturedUpdate;
    
  356.         }
    
  357.       } else {
    
  358.         // There are no base updates.
    
  359.         newFirst = newLast = capturedUpdate;
    
  360.       }
    
  361.       queue = {
    
  362.         baseState: currentQueue.baseState,
    
  363.         firstBaseUpdate: newFirst,
    
  364.         lastBaseUpdate: newLast,
    
  365.         shared: currentQueue.shared,
    
  366.         callbacks: currentQueue.callbacks,
    
  367.       };
    
  368.       workInProgress.updateQueue = queue;
    
  369.       return;
    
  370.     }
    
  371.   }
    
  372. 
    
  373.   // Append the update to the end of the list.
    
  374.   const lastBaseUpdate = queue.lastBaseUpdate;
    
  375.   if (lastBaseUpdate === null) {
    
  376.     queue.firstBaseUpdate = capturedUpdate;
    
  377.   } else {
    
  378.     lastBaseUpdate.next = capturedUpdate;
    
  379.   }
    
  380.   queue.lastBaseUpdate = capturedUpdate;
    
  381. }
    
  382. 
    
  383. function getStateFromUpdate<State>(
    
  384.   workInProgress: Fiber,
    
  385.   queue: UpdateQueue<State>,
    
  386.   update: Update<State>,
    
  387.   prevState: State,
    
  388.   nextProps: any,
    
  389.   instance: any,
    
  390. ): any {
    
  391.   switch (update.tag) {
    
  392.     case ReplaceState: {
    
  393.       const payload = update.payload;
    
  394.       if (typeof payload === 'function') {
    
  395.         // Updater function
    
  396.         if (__DEV__) {
    
  397.           enterDisallowedContextReadInDEV();
    
  398.         }
    
  399.         const nextState = payload.call(instance, prevState, nextProps);
    
  400.         if (__DEV__) {
    
  401.           if (
    
  402.             debugRenderPhaseSideEffectsForStrictMode &&
    
  403.             workInProgress.mode & StrictLegacyMode
    
  404.           ) {
    
  405.             setIsStrictModeForDevtools(true);
    
  406.             try {
    
  407.               payload.call(instance, prevState, nextProps);
    
  408.             } finally {
    
  409.               setIsStrictModeForDevtools(false);
    
  410.             }
    
  411.           }
    
  412.           exitDisallowedContextReadInDEV();
    
  413.         }
    
  414.         return nextState;
    
  415.       }
    
  416.       // State object
    
  417.       return payload;
    
  418.     }
    
  419.     case CaptureUpdate: {
    
  420.       workInProgress.flags =
    
  421.         (workInProgress.flags & ~ShouldCapture) | DidCapture;
    
  422.     }
    
  423.     // Intentional fallthrough
    
  424.     case UpdateState: {
    
  425.       const payload = update.payload;
    
  426.       let partialState;
    
  427.       if (typeof payload === 'function') {
    
  428.         // Updater function
    
  429.         if (__DEV__) {
    
  430.           enterDisallowedContextReadInDEV();
    
  431.         }
    
  432.         partialState = payload.call(instance, prevState, nextProps);
    
  433.         if (__DEV__) {
    
  434.           if (
    
  435.             debugRenderPhaseSideEffectsForStrictMode &&
    
  436.             workInProgress.mode & StrictLegacyMode
    
  437.           ) {
    
  438.             setIsStrictModeForDevtools(true);
    
  439.             try {
    
  440.               payload.call(instance, prevState, nextProps);
    
  441.             } finally {
    
  442.               setIsStrictModeForDevtools(false);
    
  443.             }
    
  444.           }
    
  445.           exitDisallowedContextReadInDEV();
    
  446.         }
    
  447.       } else {
    
  448.         // Partial state object
    
  449.         partialState = payload;
    
  450.       }
    
  451.       if (partialState === null || partialState === undefined) {
    
  452.         // Null and undefined are treated as no-ops.
    
  453.         return prevState;
    
  454.       }
    
  455.       // Merge the partial state and the previous state.
    
  456.       return assign({}, prevState, partialState);
    
  457.     }
    
  458.     case ForceUpdate: {
    
  459.       hasForceUpdate = true;
    
  460.       return prevState;
    
  461.     }
    
  462.   }
    
  463.   return prevState;
    
  464. }
    
  465. 
    
  466. export function processUpdateQueue<State>(
    
  467.   workInProgress: Fiber,
    
  468.   props: any,
    
  469.   instance: any,
    
  470.   renderLanes: Lanes,
    
  471. ): void {
    
  472.   // This is always non-null on a ClassComponent or HostRoot
    
  473.   const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
    
  474. 
    
  475.   hasForceUpdate = false;
    
  476. 
    
  477.   if (__DEV__) {
    
  478.     currentlyProcessingQueue = queue.shared;
    
  479.   }
    
  480. 
    
  481.   let firstBaseUpdate = queue.firstBaseUpdate;
    
  482.   let lastBaseUpdate = queue.lastBaseUpdate;
    
  483. 
    
  484.   // Check if there are pending updates. If so, transfer them to the base queue.
    
  485.   let pendingQueue = queue.shared.pending;
    
  486.   if (pendingQueue !== null) {
    
  487.     queue.shared.pending = null;
    
  488. 
    
  489.     // The pending queue is circular. Disconnect the pointer between first
    
  490.     // and last so that it's non-circular.
    
  491.     const lastPendingUpdate = pendingQueue;
    
  492.     const firstPendingUpdate = lastPendingUpdate.next;
    
  493.     lastPendingUpdate.next = null;
    
  494.     // Append pending updates to base queue
    
  495.     if (lastBaseUpdate === null) {
    
  496.       firstBaseUpdate = firstPendingUpdate;
    
  497.     } else {
    
  498.       lastBaseUpdate.next = firstPendingUpdate;
    
  499.     }
    
  500.     lastBaseUpdate = lastPendingUpdate;
    
  501. 
    
  502.     // If there's a current queue, and it's different from the base queue, then
    
  503.     // we need to transfer the updates to that queue, too. Because the base
    
  504.     // queue is a singly-linked list with no cycles, we can append to both
    
  505.     // lists and take advantage of structural sharing.
    
  506.     // TODO: Pass `current` as argument
    
  507.     const current = workInProgress.alternate;
    
  508.     if (current !== null) {
    
  509.       // This is always non-null on a ClassComponent or HostRoot
    
  510.       const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
    
  511.       const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
    
  512.       if (currentLastBaseUpdate !== lastBaseUpdate) {
    
  513.         if (currentLastBaseUpdate === null) {
    
  514.           currentQueue.firstBaseUpdate = firstPendingUpdate;
    
  515.         } else {
    
  516.           currentLastBaseUpdate.next = firstPendingUpdate;
    
  517.         }
    
  518.         currentQueue.lastBaseUpdate = lastPendingUpdate;
    
  519.       }
    
  520.     }
    
  521.   }
    
  522. 
    
  523.   // These values may change as we process the queue.
    
  524.   if (firstBaseUpdate !== null) {
    
  525.     // Iterate through the list of updates to compute the result.
    
  526.     let newState = queue.baseState;
    
  527.     // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
    
  528.     // from the original lanes.
    
  529.     let newLanes = NoLanes;
    
  530. 
    
  531.     let newBaseState = null;
    
  532.     let newFirstBaseUpdate = null;
    
  533.     let newLastBaseUpdate: null | Update<State> = null;
    
  534. 
    
  535.     let update: Update<State> = firstBaseUpdate;
    
  536.     do {
    
  537.       // An extra OffscreenLane bit is added to updates that were made to
    
  538.       // a hidden tree, so that we can distinguish them from updates that were
    
  539.       // already there when the tree was hidden.
    
  540.       const updateLane = removeLanes(update.lane, OffscreenLane);
    
  541.       const isHiddenUpdate = updateLane !== update.lane;
    
  542. 
    
  543.       // Check if this update was made while the tree was hidden. If so, then
    
  544.       // it's not a "base" update and we should disregard the extra base lanes
    
  545.       // that were added to renderLanes when we entered the Offscreen tree.
    
  546.       const shouldSkipUpdate = isHiddenUpdate
    
  547.         ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
    
  548.         : !isSubsetOfLanes(renderLanes, updateLane);
    
  549. 
    
  550.       if (shouldSkipUpdate) {
    
  551.         // Priority is insufficient. Skip this update. If this is the first
    
  552.         // skipped update, the previous update/state is the new base
    
  553.         // update/state.
    
  554.         const clone: Update<State> = {
    
  555.           lane: updateLane,
    
  556. 
    
  557.           tag: update.tag,
    
  558.           payload: update.payload,
    
  559.           callback: update.callback,
    
  560. 
    
  561.           next: null,
    
  562.         };
    
  563.         if (newLastBaseUpdate === null) {
    
  564.           newFirstBaseUpdate = newLastBaseUpdate = clone;
    
  565.           newBaseState = newState;
    
  566.         } else {
    
  567.           newLastBaseUpdate = newLastBaseUpdate.next = clone;
    
  568.         }
    
  569.         // Update the remaining priority in the queue.
    
  570.         newLanes = mergeLanes(newLanes, updateLane);
    
  571.       } else {
    
  572.         // This update does have sufficient priority.
    
  573. 
    
  574.         if (newLastBaseUpdate !== null) {
    
  575.           const clone: Update<State> = {
    
  576.             // This update is going to be committed so we never want uncommit
    
  577.             // it. Using NoLane works because 0 is a subset of all bitmasks, so
    
  578.             // this will never be skipped by the check above.
    
  579.             lane: NoLane,
    
  580. 
    
  581.             tag: update.tag,
    
  582.             payload: update.payload,
    
  583. 
    
  584.             // When this update is rebased, we should not fire its
    
  585.             // callback again.
    
  586.             callback: null,
    
  587. 
    
  588.             next: null,
    
  589.           };
    
  590.           newLastBaseUpdate = newLastBaseUpdate.next = clone;
    
  591.         }
    
  592. 
    
  593.         // Process this update.
    
  594.         newState = getStateFromUpdate(
    
  595.           workInProgress,
    
  596.           queue,
    
  597.           update,
    
  598.           newState,
    
  599.           props,
    
  600.           instance,
    
  601.         );
    
  602.         const callback = update.callback;
    
  603.         if (callback !== null) {
    
  604.           workInProgress.flags |= Callback;
    
  605.           if (isHiddenUpdate) {
    
  606.             workInProgress.flags |= Visibility;
    
  607.           }
    
  608.           const callbacks = queue.callbacks;
    
  609.           if (callbacks === null) {
    
  610.             queue.callbacks = [callback];
    
  611.           } else {
    
  612.             callbacks.push(callback);
    
  613.           }
    
  614.         }
    
  615.       }
    
  616.       // $FlowFixMe[incompatible-type] we bail out when we get a null
    
  617.       update = update.next;
    
  618.       if (update === null) {
    
  619.         pendingQueue = queue.shared.pending;
    
  620.         if (pendingQueue === null) {
    
  621.           break;
    
  622.         } else {
    
  623.           // An update was scheduled from inside a reducer. Add the new
    
  624.           // pending updates to the end of the list and keep processing.
    
  625.           const lastPendingUpdate = pendingQueue;
    
  626.           // Intentionally unsound. Pending updates form a circular list, but we
    
  627.           // unravel them when transferring them to the base queue.
    
  628.           const firstPendingUpdate =
    
  629.             ((lastPendingUpdate.next: any): Update<State>);
    
  630.           lastPendingUpdate.next = null;
    
  631.           update = firstPendingUpdate;
    
  632.           queue.lastBaseUpdate = lastPendingUpdate;
    
  633.           queue.shared.pending = null;
    
  634.         }
    
  635.       }
    
  636.     } while (true);
    
  637. 
    
  638.     if (newLastBaseUpdate === null) {
    
  639.       newBaseState = newState;
    
  640.     }
    
  641. 
    
  642.     queue.baseState = ((newBaseState: any): State);
    
  643.     queue.firstBaseUpdate = newFirstBaseUpdate;
    
  644.     queue.lastBaseUpdate = newLastBaseUpdate;
    
  645. 
    
  646.     if (firstBaseUpdate === null) {
    
  647.       // `queue.lanes` is used for entangling transitions. We can set it back to
    
  648.       // zero once the queue is empty.
    
  649.       queue.shared.lanes = NoLanes;
    
  650.     }
    
  651. 
    
  652.     // Set the remaining expiration time to be whatever is remaining in the queue.
    
  653.     // This should be fine because the only two other things that contribute to
    
  654.     // expiration time are props and context. We're already in the middle of the
    
  655.     // begin phase by the time we start processing the queue, so we've already
    
  656.     // dealt with the props. Context in components that specify
    
  657.     // shouldComponentUpdate is tricky; but we'll have to account for
    
  658.     // that regardless.
    
  659.     markSkippedUpdateLanes(newLanes);
    
  660.     workInProgress.lanes = newLanes;
    
  661.     workInProgress.memoizedState = newState;
    
  662.   }
    
  663. 
    
  664.   if (__DEV__) {
    
  665.     currentlyProcessingQueue = null;
    
  666.   }
    
  667. }
    
  668. 
    
  669. function callCallback(callback: () => mixed, context: any) {
    
  670.   if (typeof callback !== 'function') {
    
  671.     throw new Error(
    
  672.       'Invalid argument passed as callback. Expected a function. Instead ' +
    
  673.         `received: ${callback}`,
    
  674.     );
    
  675.   }
    
  676. 
    
  677.   callback.call(context);
    
  678. }
    
  679. 
    
  680. export function resetHasForceUpdateBeforeProcessing() {
    
  681.   hasForceUpdate = false;
    
  682. }
    
  683. 
    
  684. export function checkHasForceUpdateAfterProcessing(): boolean {
    
  685.   return hasForceUpdate;
    
  686. }
    
  687. 
    
  688. export function deferHiddenCallbacks<State>(
    
  689.   updateQueue: UpdateQueue<State>,
    
  690. ): void {
    
  691.   // When an update finishes on a hidden component, its callback should not
    
  692.   // be fired until/unless the component is made visible again. Stash the
    
  693.   // callback on the shared queue object so it can be fired later.
    
  694.   const newHiddenCallbacks = updateQueue.callbacks;
    
  695.   if (newHiddenCallbacks !== null) {
    
  696.     const existingHiddenCallbacks = updateQueue.shared.hiddenCallbacks;
    
  697.     if (existingHiddenCallbacks === null) {
    
  698.       updateQueue.shared.hiddenCallbacks = newHiddenCallbacks;
    
  699.     } else {
    
  700.       updateQueue.shared.hiddenCallbacks =
    
  701.         existingHiddenCallbacks.concat(newHiddenCallbacks);
    
  702.     }
    
  703.   }
    
  704. }
    
  705. 
    
  706. export function commitHiddenCallbacks<State>(
    
  707.   updateQueue: UpdateQueue<State>,
    
  708.   context: any,
    
  709. ): void {
    
  710.   // This component is switching from hidden -> visible. Commit any callbacks
    
  711.   // that were previously deferred.
    
  712.   const hiddenCallbacks = updateQueue.shared.hiddenCallbacks;
    
  713.   if (hiddenCallbacks !== null) {
    
  714.     updateQueue.shared.hiddenCallbacks = null;
    
  715.     for (let i = 0; i < hiddenCallbacks.length; i++) {
    
  716.       const callback = hiddenCallbacks[i];
    
  717.       callCallback(callback, context);
    
  718.     }
    
  719.   }
    
  720. }
    
  721. 
    
  722. export function commitCallbacks<State>(
    
  723.   updateQueue: UpdateQueue<State>,
    
  724.   context: any,
    
  725. ): void {
    
  726.   const callbacks = updateQueue.callbacks;
    
  727.   if (callbacks !== null) {
    
  728.     updateQueue.callbacks = null;
    
  729.     for (let i = 0; i < callbacks.length; i++) {
    
  730.       const callback = callbacks[i];
    
  731.       callCallback(callback, context);
    
  732.     }
    
  733.   }
    
  734. }