@samitouri / QOS-React / commits / d87c4d5e5c

[Fiber] Don't schedule class fibers without relevant lifecycles for commit (#9105)

* Don't schedule class fibers without relevant lifecycles for commit * Separate Update and Ref effects * Simplify the exit condition * Add missing annotation * Write conditions differently to avoid an extra check * Inline markUpdateIfNecessary() * Inline markUpdateIfAlreadyInProgress()

Dan Abramov committed Mar 4, 2017 at 04:23 UTC d87c4d5e5ca3e82ea143ce1db5b2ee448482a12d
4 files changed +67 -57
src/renderers/shared/fiber/ReactFiberClassComponent.js
+25 -21
@@ -227,22 +227,6 @@ module.exports = function(
227 }
228 }
229
230 -
231 - function markUpdate(workInProgress) {
232 - workInProgress.effectTag |= Update;
233 - }
234 -
235 - function markUpdateIfAlreadyInProgress(current: Fiber | null, workInProgress : Fiber) {
236 - // If an update was already in progress, we should schedule an Update
237 - // effect even though we're bailing out, so that cWU/cDU are called.
238 - if (current !== null) {
239 - if (workInProgress.memoizedProps !== current.memoizedProps ||
240 - workInProgress.memoizedState !== current.memoizedState) {
241 - markUpdate(workInProgress);
242 - }
243 - }
244 - }
245 -
230 function resetInputPointers(workInProgress : Fiber, instance : any) {
231 instance.props = workInProgress.memoizedProps;
232 instance.state = workInProgress.memoizedState;
@@ -276,7 +260,6 @@ module.exports = function(
260
261 // Invokes the mount life-cycles on a previously never rendered instance.
262 function mountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : void {
279 - markUpdate(workInProgress);
263 const instance = workInProgress.stateNode;
264 const state = instance.state || null;
265
@@ -310,12 +293,14 @@ module.exports = function(
293 );
294 }
295 }
296 + if (typeof instance.componentDidMount === 'function') {
297 + workInProgress.effectTag |= Update;
298 + }
299 }
300
301 // Called on a preexisting class instance. Returns false if a resumed render
302 // could be reused.
303 function resumeMountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
318 - markUpdate(workInProgress);
304 const instance = workInProgress.stateNode;
305 resetInputPointers(workInProgress, instance);
306
@@ -378,6 +363,9 @@ module.exports = function(
363 priorityLevel
364 );
365 }
366 + if (typeof instance.componentDidMount === 'function') {
367 + workInProgress.effectTag |= Update;
368 + }
369 return true;
370 }
371
@@ -447,7 +435,14 @@ module.exports = function(
435 oldState === newState &&
436 !hasContextChanged() &&
437 !(updateQueue !== null && updateQueue.hasForceUpdate)) {
450 - markUpdateIfAlreadyInProgress(current, workInProgress);
438 + // If an update was already in progress, we should schedule an Update
439 + // effect even though we're bailing out, so that cWU/cDU are called.
440 + if (typeof instance.componentDidUpdate === 'function') {
441 + if (oldProps !== current.memoizedProps ||
442 + oldState !== current.memoizedState) {
443 + workInProgress.effectTag |= Update;
444 + }
445 + }
446 return false;
447 }
448
@@ -461,12 +456,21 @@ module.exports = function(
456 );
457
458 if (shouldUpdate) {
464 - markUpdate(workInProgress);
459 if (typeof instance.componentWillUpdate === 'function') {
460 instance.componentWillUpdate(newProps, newState, newContext);
461 }
462 + if (typeof instance.componentDidUpdate === 'function') {
463 + workInProgress.effectTag |= Update;
464 + }
465 } else {
469 - markUpdateIfAlreadyInProgress(current, workInProgress);
466 + // If an update was already in progress, we should schedule an Update
467 + // effect even though we're bailing out, so that cWU/cDU are called.
468 + if (typeof instance.componentDidUpdate === 'function') {
469 + if (oldProps !== current.memoizedProps ||
470 + oldState !== current.memoizedState) {
471 + workInProgress.effectTag |= Update;
472 + }
473 + }
474
475 // If shouldComponentUpdate returned false, we should still update the
476 // memoized props/state to indicate that this work can be reused.
src/renderers/shared/fiber/ReactFiberCommitWork.js
+14 -25
@@ -87,16 +87,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
87 }
88 }
89
90 - // Only called during update. It's ok to throw.
91 - function detachRefIfNeeded(current : Fiber | null, finishedWork : Fiber) {
92 - if (current) {
93 - const currentRef = current.ref;
94 - if (currentRef !== null && currentRef !== finishedWork.ref) {
95 - currentRef(null);
96 - }
97 - }
98 - }
99 -
90 function getHostParent(fiber : Fiber) : I | C {
91 let parent = fiber.return;
92 while (parent !== null) {
@@ -381,7 +371,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
371 function commitWork(current : Fiber | null, finishedWork : Fiber) : void {
372 switch (finishedWork.tag) {
373 case ClassComponent: {
384 - detachRefIfNeeded(current, finishedWork);
374 return;
375 }
376 case HostComponent: {
@@ -398,7 +387,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
387 commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
388 }
389 }
401 - detachRefIfNeeded(current, finishedWork);
390 return;
391 }
392 case HostText: {
@@ -435,15 +423,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
423 const instance = finishedWork.stateNode;
424 if (finishedWork.effectTag & Update) {
425 if (current === null) {
438 - if (typeof instance.componentDidMount === 'function') {
439 - instance.componentDidMount();
440 - }
426 + instance.componentDidMount();
427 } else {
442 - if (typeof instance.componentDidUpdate === 'function') {
443 - const prevProps = current.memoizedProps;
444 - const prevState = current.memoizedState;
445 - instance.componentDidUpdate(prevProps, prevState);
446 - }
428 + const prevProps = current.memoizedProps;
429 + const prevState = current.memoizedState;
430 + instance.componentDidUpdate(prevProps, prevState);
431 }
432 }
433 if ((finishedWork.effectTag & Callback) && finishedWork.updateQueue !== null) {
@@ -495,10 +479,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
479 }
480 }
481
498 - function commitRef(finishedWork : Fiber) {
499 - if (finishedWork.tag !== ClassComponent && finishedWork.tag !== HostComponent) {
500 - return;
501 - }
482 + function commitAttachRef(finishedWork : Fiber) {
483 const ref = finishedWork.ref;
484 if (ref !== null) {
485 const instance = getPublicInstance(finishedWork.stateNode);
@@ -506,12 +487,20 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
487 }
488 }
489
490 + function commitDetachRef(current : Fiber) {
491 + const currentRef = current.ref;
492 + if (currentRef !== null) {
493 + currentRef(null);
494 + }
495 + }
496 +
497 return {
498 commitPlacement,
499 commitDeletion,
500 commitWork,
501 commitLifeCycles,
514 - commitRef,
502 + commitAttachRef,
503 + commitDetachRef,
504 };
505
506 };
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+9 -2
@@ -85,6 +85,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
85 workInProgress.effectTag |= Update;
86 }
87
88 + function markRef(workInProgress : Fiber) {
89 + workInProgress.effectTag |= Ref;
90 + }
91 +
92 function appendAllYields(yields : Array<mixed>, workInProgress : Fiber) {
93 let node = workInProgress.stateNode;
94 if (node) {
@@ -231,9 +235,12 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
235 workInProgress.updateQueue = (updatePayload : any);
236 // If the update payload indicates that there is a change or if there
237 // is a new ref we mark this as an update.
234 - if (updatePayload || current.ref !== workInProgress.ref) {
238 + if (updatePayload) {
239 markUpdate(workInProgress);
240 }
241 + if (current.ref !== workInProgress.ref) {
242 + markRef(workInProgress);
243 + }
244 } else {
245 if (!newProps) {
246 invariant(
@@ -270,7 +277,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
277 workInProgress.stateNode = instance;
278 if (workInProgress.ref !== null) {
279 // If there is a ref on a host node we need to schedule a callback
273 - workInProgress.effectTag |= Ref;
280 + markRef(workInProgress);
281 }
282 }
283 return null;
src/renderers/shared/fiber/ReactFiberScheduler.js
+19 -9
@@ -146,7 +146,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
146 commitDeletion,
147 commitWork,
148 commitLifeCycles,
149 - commitRef,
149 + commitAttachRef,
150 + commitDetachRef,
151 } = ReactFiberCommitWork(config, captureError);
152 const {
153 scheduleAnimationCallback: hostScheduleAnimationCallback,
@@ -294,16 +295,23 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
295 ReactDebugCurrentFiber.current = nextEffect;
296 }
297
297 - if (nextEffect.effectTag & ContentReset) {
298 + const effectTag = nextEffect.effectTag;
299 + if (effectTag & ContentReset) {
300 config.resetTextContent(nextEffect.stateNode);
301 }
302
303 + if (effectTag & Ref) {
304 + const current = nextEffect.alternate;
305 + if (current !== null) {
306 + commitDetachRef(current);
307 + }
308 + }
309 +
310 // The following switch statement is only concerned about placement,
311 // updates, and deletions. To avoid needing to add a case for every
312 // possible bitmap value, we remove the secondary effects from the
313 // effect tag and switch on that value.
305 - let primaryEffectTag =
306 - nextEffect.effectTag & ~(Callback | Err | ContentReset | Ref);
314 + let primaryEffectTag = effectTag & ~(Callback | Err | ContentReset | Ref);
315 switch (primaryEffectTag) {
316 case Placement: {
317 commitPlacement(nextEffect);
@@ -349,17 +357,19 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
357
358 function commitAllLifeCycles() {
359 while (nextEffect !== null) {
352 - const current = nextEffect.alternate;
360 + const effectTag = nextEffect.effectTag;
361 +
362 // Use Task priority for lifecycle updates
354 - if (nextEffect.effectTag & (Update | Callback)) {
363 + if (effectTag & (Update | Callback)) {
364 + const current = nextEffect.alternate;
365 commitLifeCycles(current, nextEffect);
366 }
367
358 - if (nextEffect.effectTag & Ref) {
359 - commitRef(nextEffect);
368 + if (effectTag & Ref) {
369 + commitAttachRef(nextEffect);
370 }
371
362 - if (nextEffect.effectTag & Err) {
372 + if (effectTag & Err) {
373 commitErrorHandling(nextEffect);
374 }
375