Move props equality check into each branch
Now we only bail out once we've entered each branch. This has some repetition with regard to hasContextChanged but I'm hoping we can just get rid of all those branches at some point since they're mostly for backwards compatibility right now.
Sebastian Markbage committed
Dec 20, 2016 at 23:47 UTC
21dccad47f26eed4709f59aaf9a897c358f957aa
1 file changed
+51
-24
src/renderers/shared/fiber/ReactFiberBeginWork.js
+51
-24
@@ -168,6 +168,15 @@ module.exports = function<T, P, I, TI, C, CX>(
168
169
function updateFragment(current, workInProgress) {
170
var nextChildren = workInProgress.pendingProps;
171
+ if (hasContextChanged()) {
172
+ // Normally we can bail out on props equality but if context has changed
173
+ // we don't do the bailout and we have to reuse existing props instead.
174
+ if (nextChildren === null) {
175
+ nextChildren = current && current.memoizedProps;
176
+ }
177
+ } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) {
178
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
179
+ }
180
reconcileChildren(current, workInProgress, nextChildren);
181
return workInProgress.child;
182
}
@@ -282,8 +291,22 @@ module.exports = function<T, P, I, TI, C, CX>(
291
}
292
293
function updateHostComponent(current, workInProgress) {
285
- const nextProps = workInProgress.pendingProps;
294
+ let nextProps = workInProgress.pendingProps;
295
const prevProps = current ? current.memoizedProps : null;
296
+ const memoizedProps = workInProgress.memoizedProps;
297
+ if (hasContextChanged()) {
298
+ // Normally we can bail out on props equality but if context has changed
299
+ // we don't do the bailout and we have to reuse existing props instead.
300
+ if (nextProps === null) {
301
+ nextProps = prevProps;
302
+ if (!nextProps) {
303
+ throw new Error('We should always have pending or current props.');
304
+ }
305
+ }
306
+ } else if (nextProps === null || memoizedProps === nextProps) {
307
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
308
+ }
309
+
310
let nextChildren = nextProps.children;
311
const isDirectTextChild = shouldSetTextContent(nextProps);
312
@@ -376,11 +399,20 @@ module.exports = function<T, P, I, TI, C, CX>(
399
}
400
401
function updateCoroutineComponent(current, workInProgress) {
379
- var coroutine = (workInProgress.pendingProps : ?ReactCoroutine);
380
- if (!coroutine) {
381
- throw new Error('Should be resolved by now');
402
+ var nextCoroutine = (workInProgress.pendingProps : null | ReactCoroutine);
403
+ if (hasContextChanged()) {
404
+ // Normally we can bail out on props equality but if context has changed
405
+ // we don't do the bailout and we have to reuse existing props instead.
406
+ if (nextCoroutine === null) {
407
+ nextCoroutine = current && current.memoizedProps;
408
+ if (!nextCoroutine) {
409
+ throw new Error('We should always have pending or current props.');
410
+ }
411
+ }
412
+ } else if (nextCoroutine === null || workInProgress.memoizedProps === nextCoroutine) {
413
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
414
}
383
- reconcileChildren(current, workInProgress, coroutine.children);
415
+ reconcileChildren(current, workInProgress, nextCoroutine.children);
416
// This doesn't take arbitrary time so we could synchronously just begin
417
// eagerly do the work of workInProgress.child as an optimization.
418
return workInProgress.child;
@@ -389,7 +421,20 @@ module.exports = function<T, P, I, TI, C, CX>(
421
function updatePortalComponent(current, workInProgress) {
422
pushHostContainer(workInProgress.stateNode.containerInfo);
423
const priorityLevel = workInProgress.pendingWorkPriority;
392
- const nextChildren = workInProgress.pendingProps;
424
+ let nextChildren = workInProgress.pendingProps;
425
+ if (hasContextChanged()) {
426
+ // Normally we can bail out on props equality but if context has changed
427
+ // we don't do the bailout and we have to reuse existing props instead.
428
+ if (nextChildren === null) {
429
+ nextChildren = current && current.memoizedProps;
430
+ if (!nextChildren) {
431
+ throw new Error('We should always have pending or current props.');
432
+ }
433
+ }
434
+ } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) {
435
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
436
+ }
437
+
438
if (!current) {
439
// Portals are special because we don't append the children during mount
440
// but at commit. Therefore we need to track insertions which the normal
@@ -536,24 +581,6 @@ module.exports = function<T, P, I, TI, C, CX>(
581
workInProgress.child = workInProgress.progressedChild;
582
}
583
539
- const pendingProps = workInProgress.pendingProps;
540
- const memoizedProps = workInProgress.memoizedProps;
541
- const updateQueue = workInProgress.updateQueue;
542
-
543
- // This is kept as a single expression to take advantage of short-circuiting.
544
- const hasNewProps = (
545
- pendingProps !== null && ( // hasPendingProps && (
546
- memoizedProps === null || // hasNoMemoizedProps ||
547
- pendingProps !== memoizedProps // memoizedPropsDontMatch
548
- ) // )
549
- );
550
- if (!hasNewProps) {
551
- const hasUpdate = updateQueue && hasPendingUpdate(updateQueue, priorityLevel);
552
- if (!hasUpdate && !hasContextChanged()) {
553
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
554
- }
555
- }
556
-
584
switch (workInProgress.tag) {
585
case IndeterminateComponent:
586
return mountIndeterminateComponent(current, workInProgress, priorityLevel);