@samitouri / QOS-React-2 / commits / 24a83a5eeb

Extract commit phase passes into separate functions

By splitting these out, we ensure that they can be optimized by the JS engine.

Andrew Clark committed Dec 7, 2016 at 11:42 UTC 24a83a5eeb1ccf4da1bdd97166d6c7c94d821bd8
2 files changed +120 -114
src/renderers/shared/fiber/ReactFiberCommitWork.js
+1 -1
@@ -35,7 +35,7 @@ var {
35
36 module.exports = function<T, P, I, TI, C>(
37 config : HostConfig<T, P, I, TI, C>,
38 - captureError : (failedFiber : Fiber, error: Error) => Fiber | null
38 + captureError : (failedFiber : Fiber, error: Error) => ?Fiber
39 ) {
40
41 const commitUpdate = config.commitUpdate;
src/renderers/shared/fiber/ReactFiberScheduler.js
+119 -113
@@ -87,7 +87,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
87 let nextUnitOfWork : ?Fiber = null;
88 let nextPriorityLevel : PriorityLevel = NoWork;
89
90 - let pendingCommit : Fiber | null = null;
90 + // The next fiber with an effect, during the commit phase.
91 + let nextEffect : ?Fiber = null;
92 +
93 + let pendingCommit : ?Fiber = null;
94
95 // Linked list of roots with scheduled work on them.
96 let nextScheduledRoot : ?FiberRoot = null;
@@ -172,7 +175,97 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
175 return null;
176 }
177
175 - // Returns true if the commit phase finishes without any errors.
178 + function commitAllHostEffects(finishedWork : Fiber) {
179 + while (nextEffect) {
180 + if (nextEffect.effectTag & ContentReset) {
181 + config.resetTextContent(nextEffect.stateNode);
182 + }
183 +
184 + // The following switch statement is only concerned about placement,
185 + // updates, and deletions. To avoid needing to add a case for every
186 + // possible bitmap value, we remove the secondary effects from the
187 + // effect tag and switch on that value.
188 + let primaryEffectTag = nextEffect.effectTag & ~(Callback | Err | ContentReset);
189 + switch (primaryEffectTag) {
190 + case Placement: {
191 + commitPlacement(nextEffect);
192 + // Clear the "placement" from effect tag so that we know that this is inserted, before
193 + // any life-cycles like componentDidMount gets called.
194 + // TODO: findDOMNode doesn't rely on this any more but isMounted
195 + // does and isMounted is deprecated anyway so we should be able
196 + // to kill this.
197 + nextEffect.effectTag &= ~Placement;
198 + break;
199 + }
200 + case PlacementAndUpdate: {
201 + // Placement
202 + commitPlacement(nextEffect);
203 + // Clear the "placement" from effect tag so that we know that this is inserted, before
204 + // any life-cycles like componentDidMount gets called.
205 + nextEffect.effectTag &= ~Placement;
206 +
207 + // Update
208 + const current = nextEffect.alternate;
209 + commitWork(current, nextEffect);
210 + break;
211 + }
212 + case Update: {
213 + const current = nextEffect.alternate;
214 + commitWork(current, nextEffect);
215 + break;
216 + }
217 + case Deletion: {
218 + isUnmounting = true;
219 + commitDeletion(nextEffect);
220 + isUnmounting = false;
221 + break;
222 + }
223 + }
224 + nextEffect = nextEffect.nextEffect;
225 + }
226 +
227 + // If the root itself had an effect, we perform that since it is
228 + // not part of the effect list.
229 + if (finishedWork.effectTag !== NoEffect) {
230 + const current = finishedWork.alternate;
231 + commitWork(current, finishedWork);
232 + }
233 + }
234 +
235 + function commitAllLifeCycles(finishedWork : Fiber) {
236 + while (nextEffect) {
237 + const current = nextEffect.alternate;
238 + // Use Task priority for lifecycle updates
239 + if (nextEffect.effectTag & (Update | Callback)) {
240 + commitLifeCycles(current, nextEffect);
241 + }
242 +
243 + if (nextEffect.effectTag & Err) {
244 + commitErrorHandling(nextEffect);
245 + }
246 +
247 + const next = nextEffect.nextEffect;
248 + // Ensure that we clean these up so that we don't accidentally keep them.
249 + // I'm not actually sure this matters because we can't reset firstEffect
250 + // and lastEffect since they're on every node, not just the effectful
251 + // ones. So we have to clean everything as we reuse nodes anyway.
252 + nextEffect.nextEffect = null;
253 + // Ensure that we reset the effectTag here so that we can rely on effect
254 + // tags to reason about the current life-cycle.
255 + nextEffect = next;
256 + }
257 +
258 + // If the root itself had an effect, we perform that since it is
259 + // not part of the effect list.
260 + if (finishedWork.effectTag !== NoEffect) {
261 + const current = finishedWork.alternate;
262 + commitLifeCycles(current, finishedWork);
263 + if (finishedWork.effectTag & Err) {
264 + commitErrorHandling(finishedWork);
265 + }
266 + }
267 + }
268 +
269 function commitAllWork(finishedWork : Fiber) {
270 // We keep track of this so that captureError can collect any boundaries
271 // that capture an error during the commit phase. The reason these aren't
@@ -196,129 +289,42 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
289
290 prepareForCommit();
291
199 - // Commit all the side-effects within a tree.
200 - // First, we'll perform all the host insertions, updates, deletions and
292 + // Commit all the side-effects within a tree. We'll do this in two passes.
293 + // The first pass performs all the host insertions, updates, deletions and
294 // ref unmounts.
202 - let effectfulFiber = finishedWork.firstEffect;
203 - pass1: while (true) {
204 - let didHandleRoot = false;
295 + nextEffect = finishedWork.firstEffect;
296 + while (true) {
297 try {
206 - while (effectfulFiber) {
207 - if (effectfulFiber.effectTag & ContentReset) {
208 - config.resetTextContent(effectfulFiber.stateNode);
209 - }
210 -
211 - // The following switch statement is only concerned about placement,
212 - // updates, and deletions. To avoid needing to add a case for every
213 - // possible bitmap value, we remove the secondary effects from the
214 - // effect tag and switch on that value.
215 - let primaryEffectTag = effectfulFiber.effectTag & ~(Callback | Err | ContentReset);
216 - switch (primaryEffectTag) {
217 - case Placement: {
218 - commitPlacement(effectfulFiber);
219 - // Clear the "placement" from effect tag so that we know that this is inserted, before
220 - // any life-cycles like componentDidMount gets called.
221 - // TODO: findDOMNode doesn't rely on this any more but isMounted
222 - // does and isMounted is deprecated anyway so we should be able
223 - // to kill this.
224 - effectfulFiber.effectTag &= ~Placement;
225 - break;
226 - }
227 - case PlacementAndUpdate: {
228 - // Placement
229 - commitPlacement(effectfulFiber);
230 - // Clear the "placement" from effect tag so that we know that this is inserted, before
231 - // any life-cycles like componentDidMount gets called.
232 - effectfulFiber.effectTag &= ~Placement;
233 -
234 - // Update
235 - const current = effectfulFiber.alternate;
236 - commitWork(current, effectfulFiber);
237 - break;
238 - }
239 - case Update: {
240 - const current = effectfulFiber.alternate;
241 - commitWork(current, effectfulFiber);
242 - break;
243 - }
244 - case Deletion: {
245 - isUnmounting = true;
246 - commitDeletion(effectfulFiber);
247 - isUnmounting = false;
248 - break;
249 - }
250 - }
251 - effectfulFiber = effectfulFiber.nextEffect;
252 - }
253 -
254 - // Finally if the root itself had an effect, we perform that since it is
255 - // not part of the effect list.
256 - if (!didHandleRoot && finishedWork.effectTag !== NoEffect) {
257 - didHandleRoot = true;
258 - const current = finishedWork.alternate;
259 - commitWork(current, finishedWork);
260 - }
298 + commitAllHostEffects(finishedWork);
299 } catch (error) {
262 - captureError(effectfulFiber || null, error);
263 - if (effectfulFiber) {
264 - effectfulFiber = effectfulFiber.nextEffect;
265 - }
300 + captureError(nextEffect, error);
301 // Clean-up
302 isUnmounting = false;
268 - continue pass1;
303 + if (nextEffect) {
304 + nextEffect = nextEffect.nextEffect;
305 + continue;
306 + }
307 }
308 break;
309 }
310
311 resetAfterCommit();
312
275 - // Next, we'll perform all life-cycles and ref callbacks. Life-cycles
276 - // happens as a separate pass so that all effects in the entire tree have
277 - // already been invoked.
278 - effectfulFiber = finishedWork.firstEffect;
279 - pass2: while (true) {
280 - let didHandleRoot = false;
313 + // In the second pass we'll perform all life-cycles and ref callbacks.
314 + // Life-cycles happen as a separate pass so that all placements, updates,
315 + // and deletions in the entire tree have already been invoked.
316 + nextEffect = finishedWork.firstEffect;
317 + while (true) {
318 try {
282 - while (effectfulFiber) {
283 - const current = effectfulFiber.alternate;
284 - // Use Task priority for lifecycle updates
285 - if (effectfulFiber.effectTag & (Update | Callback)) {
286 - commitLifeCycles(current, effectfulFiber);
287 - }
288 -
289 - if (effectfulFiber.effectTag & Err) {
290 - commitErrorHandling(effectfulFiber);
291 - }
292 -
293 - const next = effectfulFiber.nextEffect;
294 - // Ensure that we clean these up so that we don't accidentally keep them.
295 - // I'm not actually sure this matters because we can't reset firstEffect
296 - // and lastEffect since they're on every node, not just the effectful
297 - // ones. So we have to clean everything as we reuse nodes anyway.
298 - effectfulFiber.nextEffect = null;
299 - // Ensure that we reset the effectTag here so that we can rely on effect
300 - // tags to reason about the current life-cycle.
301 - effectfulFiber = next;
302 - }
303 -
304 - // Finally if the root itself had an effect, we perform that since it is
305 - // not part of the effect list.
306 - if (!didHandleRoot && finishedWork.effectTag !== NoEffect) {
307 - didHandleRoot = true;
308 - const current = finishedWork.alternate;
309 - commitLifeCycles(current, finishedWork);
310 - if (finishedWork.effectTag & Err) {
311 - commitErrorHandling(finishedWork);
312 - }
313 - }
319 + commitAllLifeCycles(finishedWork, nextEffect);
320 } catch (error) {
315 - captureError(effectfulFiber || null, error);
316 - if (effectfulFiber) {
317 - const next = effectfulFiber.nextEffect;
318 - effectfulFiber.nextEffect = null;
319 - effectfulFiber = next;
321 + captureError(nextEffect || null, error);
322 + if (nextEffect) {
323 + const next = nextEffect.nextEffect;
324 + nextEffect.nextEffect = null;
325 + nextEffect = next;
326 }
321 - continue pass2;
327 + continue;
328 }
329 break;
330 }
@@ -716,14 +722,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
722 }
723
724 // Returns the boundary that captured the error, or null if the error is ignored
719 - function captureError(failedWork : Fiber | null, error : Error) : Fiber | null {
725 + function captureError(failedWork : ?Fiber, error : Error) : ?Fiber {
726 // It is no longer valid because we exited the user code.
727 ReactCurrentOwner.current = null;
728 // It is no longer valid because this unit of work failed.
729 nextUnitOfWork = null;
730
731 // Search for the nearest error boundary.
726 - let boundary : Fiber | null = null;
732 + let boundary : ?Fiber = null;
733 if (failedWork) {
734 // Host containers are a special case. If the failed work itself is a host
735 // container, then it acts as its own boundary. In all other cases, we