@samitouri / QOS-React-2 / commits / cf86cd8ebf

Fix dev/prod mismatch in context === bailout

Fixes #5005.

Ben Alpert committed Sep 30, 2015 at 16:43 UTC cf86cd8ebfab28d01e5a2b8dae00e7993fd9d7cc
2 files changed +54 -11
src/renderers/dom/shared/ReactDOMComponent.js
+31 -11
@@ -480,14 +480,12 @@ function validateDangerousTag(tag) {
480 }
481 }
482
483 -function processChildContext(context, inst) {
484 - if (__DEV__) {
485 - // Pass down our tag name to child components for validation purposes
486 - context = assign({}, context);
487 - var info = context[validateDOMNesting.ancestorInfoContextKey];
488 - context[validateDOMNesting.ancestorInfoContextKey] =
489 - validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
490 - }
483 +function processChildContextDev(context, inst) {
484 + // Pass down our tag name to child components for validation purposes
485 + context = assign({}, context);
486 + var info = context[validateDOMNesting.ancestorInfoContextKey];
487 + context[validateDOMNesting.ancestorInfoContextKey] =
488 + validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
489 return context;
490 }
491
@@ -519,6 +517,10 @@ function ReactDOMComponent(tag) {
517 this._wrapperState = null;
518 this._topLevelWrapper = null;
519 this._nodeWithLegacyProperties = null;
520 + if (__DEV__) {
521 + this._unprocessedContextDev = null;
522 + this._processedContextDev = null;
523 + }
524 }
525
526 ReactDOMComponent.displayName = 'ReactDOMComponent';
@@ -588,6 +590,12 @@ ReactDOMComponent.Mixin = {
590 }
591 }
592
593 + if (__DEV__) {
594 + this._unprocessedContextDev = context;
595 + this._processedContextDev = processChildContextDev(context, this);
596 + context = this._processedContextDev;
597 + }
598 +
599 var mountImage;
600 if (transaction.useCreateElement) {
601 var ownerDocument = context[ReactMount.ownerDocumentContextKey];
@@ -719,7 +727,7 @@ ReactDOMComponent.Mixin = {
727 var mountImages = this.mountChildren(
728 childrenToUse,
729 transaction,
722 - processChildContext(context, this)
730 + context
731 );
732 ret = mountImages.join('');
733 }
@@ -759,7 +767,7 @@ ReactDOMComponent.Mixin = {
767 var mountImages = this.mountChildren(
768 childrenToUse,
769 transaction,
762 - processChildContext(context, this)
770 + context
771 );
772 for (var i = 0; i < mountImages.length; i++) {
773 el.appendChild(mountImages[i]);
@@ -821,13 +829,25 @@ ReactDOMComponent.Mixin = {
829 break;
830 }
831
832 + if (__DEV__) {
833 + // If the context is reference-equal to the old one, pass down the same
834 + // processed object so the update bailout in ReactReconciler behaves
835 + // correctly (and identically in dev and prod). See #5005.
836 + if (this._unprocessedContextDev !== context) {
837 + this._unprocessedContextDev = context;
838 + this._processedContextDev = processChildContextDev(context, this);
839 + }
840 + context = this._processedContextDev;
841 + }
842 +
843 +
844 assertValidProps(this, nextProps);
845 this._updateDOMProperties(lastProps, nextProps, transaction, null);
846 this._updateDOMChildren(
847 lastProps,
848 nextProps,
849 transaction,
830 - processChildContext(context, this)
850 + context
851 );
852
853 if (!canDefineProperty && this._nodeWithLegacyProperties) {
src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
+23
@@ -607,6 +607,29 @@ describe('ReactCompositeComponent', function() {
607 expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar');
608 });
609
610 + it('should skip update when rerendering element in container', function() {
611 + var Parent = React.createClass({
612 + render: function() {
613 + return <div>{this.props.children}</div>;
614 + },
615 + });
616 +
617 + var childRenders = 0;
618 + var Child = React.createClass({
619 + render: function() {
620 + childRenders++;
621 + return <div />;
622 + },
623 + });
624 +
625 + var container = document.createElement('div');
626 + var child = <Child />;
627 +
628 + ReactDOM.render(<Parent>{child}</Parent>, container);
629 + ReactDOM.render(<Parent>{child}</Parent>, container);
630 + expect(childRenders).toBe(1);
631 + });
632 +
633 it('should pass context when re-rendered for static child', function() {
634 var parentInstance = null;
635 var childInstance = null;