@samitouri / QOS-React-2 / commits / 62b52e008e

Don't reconcile children unneccessarily

We've talked about this perf optimization for a while now. This prevents us from re-reconciling components that are above the component being reconciled in the owner hierarchy.

Pete Hunt committed Jan 23, 2014 at 13:17 UTC 62b52e008e139a75267a40c626d76683bca7f3ab
2 files changed +54 -10
src/core/ReactCompositeComponent.js
+7
@@ -1060,6 +1060,13 @@ var ReactCompositeComponentMixin = {
1060 },
1061
1062 receiveComponent: function(nextComponent, transaction) {
1063 + if (nextComponent === this) {
1064 + // Since props and context are immutable after the component is
1065 + // mounted, we can do a cheap identity compare here to determine
1066 + // if this is a superfluous reconcile.
1067 + return;
1068 + }
1069 +
1070 this._pendingContext = nextComponent._currentContext;
1071 ReactComponent.Mixin.receiveComponent.call(
1072 this,
src/core/__tests__/ReactUpdates-test.js
+47 -10
@@ -338,6 +338,39 @@ describe('ReactUpdates', function() {
338 expect(childRenderCount).toBe(2);
339 });
340
341 + it('should not reconcile children passed via props', function() {
342 + var numMiddleRenders = 0;
343 + var numBottomRenders = 0;
344 +
345 + var Top = React.createClass({
346 + render: function() {
347 + return <Middle><Bottom /></Middle>;
348 + }
349 + });
350 +
351 + var Middle = React.createClass({
352 + componentDidMount: function() {
353 + this.forceUpdate();
354 + },
355 +
356 + render: function() {
357 + numMiddleRenders++;
358 + return <div>{this.props.children}</div>;
359 + }
360 + });
361 +
362 + var Bottom = React.createClass({
363 + render: function() {
364 + numBottomRenders++;
365 + return <span />;
366 + }
367 + });
368 +
369 + ReactTestUtils.renderIntoDocument(<Top />);
370 + expect(numMiddleRenders).toBe(2);
371 + expect(numBottomRenders).toBe(1);
372 + });
373 +
374 it('should flow updates correctly', function() {
375 var willUpdates = [];
376 var didUpdates = [];
@@ -406,11 +439,9 @@ describe('ReactUpdates', function() {
439 var root = <App />;
440 ReactTestUtils.renderIntoDocument(root);
441
409 - function expectUpdates(sequence) {
410 - // didUpdate() occurs in reverse order
411 - didUpdates.reverse();
412 - expect(willUpdates).toEqual(didUpdates);
413 - expect(willUpdates).toEqual(sequence);
442 + function expectUpdates(desiredWillUpdates, desiredDidUpdates) {
443 + expect(willUpdates).toEqual(desiredWillUpdates);
444 + expect(didUpdates).toEqual(desiredDidUpdates);
445 willUpdates.length = 0;
446 didUpdates.length = 0;
447 }
@@ -419,7 +450,7 @@ describe('ReactUpdates', function() {
450 c.setState({x: 1});
451 }
452
422 - function testUpdates(components, expectation) {
453 + function testUpdates(components, desiredWillUpdates, desiredDidUpdates) {
454 var i;
455
456 ReactUpdates.batchedUpdates(function() {
@@ -428,7 +459,7 @@ describe('ReactUpdates', function() {
459 }
460 });
461
431 - expectUpdates(expectation);
462 + expectUpdates(desiredWillUpdates, desiredDidUpdates);
463
464 // Try them in reverse order
465
@@ -438,22 +469,28 @@ describe('ReactUpdates', function() {
469 }
470 });
471
441 - expectUpdates(expectation);
472 + expectUpdates(desiredWillUpdates, desiredDidUpdates);
473 }
474
475 testUpdates(
476 [root.refs.switcher.refs.box, root.refs.switcher],
446 - ['Switcher', 'Box', 'Child']
477 + // Owner-child relationships have inverse will and did
478 + ['Switcher', 'Box'],
479 + ['Box', 'Switcher']
480 );
481
482 testUpdates(
483 [root.refs.child, root.refs.switcher.refs.box],
484 + // Not owner-child so reconcile independently
485 + ['Box', 'Child'],
486 ['Box', 'Child']
487 );
488
489 testUpdates(
490 [root.refs.child, root.refs.switcher],
456 - ['Switcher', 'Box', 'Child']
491 + // Switcher owns Box and Child, Box does not own Child
492 + ['Switcher', 'Box', 'Child'],
493 + ['Box', 'Switcher', 'Child']
494 );
495 });
496 });