[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