1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| function updateReducerImpl<S, A>( hook: Hook, current: Hook, reducer: (S, A) => S, ): [S, Dispatch<A>] { const queue = hook.queue;
queue.lastRenderedReducer = reducer; let baseQueue = hook.baseQueue;
const baseState = hook.baseState; if (baseQueue === null) { // ... } else { // We have a queue to process. const first = baseQueue.next; let newState = baseState;
let newBaseState = null; let newBaseQueueFirst = null; let newBaseQueueLast: Update<S, A> | null = null; let update = first; let didReadFromEntangledAsyncAction = false; do { const updateLane = removeLanes(update.lane, OffscreenLane); const isHiddenUpdate = updateLane !== update.lane;
const shouldSkipUpdate = isHiddenUpdate ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane) : !isSubsetOfLanes(renderLanes, updateLane);
if (shouldSkipUpdate) { // Priority is insufficient. Skip this update. If this is the first // skipped update, the previous update/state is the new base // update/state. const clone: Update<S, A> = { lane: updateLane, revertLane: update.revertLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; if (newBaseQueueLast === null) { newBaseQueueFirst = newBaseQueueLast = clone; newBaseState = newState; } else { newBaseQueueLast = newBaseQueueLast.next = clone; }
currentlyRenderingFiber.lanes = mergeLanes( currentlyRenderingFiber.lanes, updateLane, ); markSkippedUpdateLanes(updateLane); } else { // This update does have sufficient priority.
// 这里查看是否是一个乐观更新,通过reverLane属性来检查 const revertLane = update.revertLane; if (!enableAsyncActions || revertLane === NoLane) { // 不是乐观更新 if (newBaseQueueLast !== null) { const clone: Update<S, A> = { // This update is going to be committed so we never want uncommit // it. Using NoLane works because 0 is a subset of all bitmasks, so // this will never be skipped by the check above. lane: NoLane, revertLane: NoLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; newBaseQueueLast = newBaseQueueLast.next = clone; }
if (updateLane === peekEntangledActionLane()) { didReadFromEntangledAsyncAction = true; } } else { // This is an optimistic update. If the "revert" priority is // sufficient, don't apply the update. Otherwise, apply the update, // but leave it in the queue so it can be either reverted or // rebased in a subsequent render. // 这是乐观更新 if (isSubsetOfLanes(renderLanes, revertLane)) { // The transition that this optimistic update is associated with // has finished. Pretend the update doesn't exist by skipping // over it. update = update.next;
// Check if this update is part of a pending async action. If so, // we'll need to suspend until the action has finished, so that it's // batched together with future updates in the same action. if (revertLane === peekEntangledActionLane()) { didReadFromEntangledAsyncAction = true; } continue; } else { const clone: Update<S, A> = { // Once we commit an optimistic update, we shouldn't uncommit it // until the transition it is associated with has finished // (represented by revertLane). Using NoLane here works because 0 // is a subset of all bitmasks, so this will never be skipped by // the check above. lane: NoLane, // Reuse the same revertLane so we know when the transition // has finished. revertLane: update.revertLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; if (newBaseQueueLast === null) { newBaseQueueFirst = newBaseQueueLast = clone; newBaseState = newState; } else { newBaseQueueLast = newBaseQueueLast.next = clone; } // Update the remaining priority in the queue. // TODO: Don't need to accumulate this. Instead, we can remove // renderLanes from the original lanes. currentlyRenderingFiber.lanes = mergeLanes( currentlyRenderingFiber.lanes, revertLane, ); markSkippedUpdateLanes(revertLane); } }
// Process this update. const action = update.action; if (shouldDoubleInvokeUserFnsInHooksDEV) { reducer(newState, action); } if (update.hasEagerState) { // If this update is a state update (not a reducer) and was processed eagerly, // we can use the eagerly computed state newState = ((update.eagerState: any): S); } else { newState = reducer(newState, action); } } update = update.next; } while (update !== null && update !== first);
if (newBaseQueueLast === null) { newBaseState = newState; } else { newBaseQueueLast.next = (newBaseQueueFirst: any); }
// Mark that the fiber performed work, but only if the new state is // different from the current state. if (!is(newState, hook.memoizedState)) { markWorkInProgressReceivedUpdate();
// 这里就是最重要的 如果状态变了并且依赖于正在进行的async action,直接抛出promise等待suspense处理 if (didReadFromEntangledAsyncAction) { const entangledActionThenable = peekEntangledActionThenable(); if (entangledActionThenable !== null) { // TODO: Instead of the throwing the thenable directly, throw a // special object like `use` does so we can detect if it's captured // by userspace. throw entangledActionThenable; } } }
hook.memoizedState = newState; hook.baseState = newBaseState; hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState; }
if (baseQueue === null) { // `queue.lanes` is used for entangling transitions. We can set it back to // zero once the queue is empty. queue.lanes = NoLanes; }
const dispatch: Dispatch<A> = (queue.dispatch: any); return [hook.memoizedState, dispatch]; }
|