@samitouri / QOS-React-2 / commits / 7d44917531

Switch to parent-based context. Fixes #2112.

Jim committed Apr 7, 2015 at 14:02 UTC 7d4491753158afc839f2934367ad2f7878ff7283
5 files changed +36 -287
src/classic/element/ReactElement.js
-4
@@ -99,10 +99,6 @@ var ReactElement = function(type, key, ref, owner, context, props) {
99 // Record the component responsible for creating this element.
100 this._owner = owner;
101
102 - // TODO: Deprecate withContext, and then the context becomes accessible
103 - // through the owner.
104 - this._context = context;
105 -
102 if (__DEV__) {
103 // The validation flag and props are currently mutative. We put them on
104 // an external backing store so that we can freeze the whole object.
src/classic/element/__tests__/ReactElement-test.js
-24
@@ -86,30 +86,6 @@ describe('ReactElement', function() {
86 expect(element.props).toEqual({foo:'56'});
87 });
88
89 - it('preserves the legacy context on the element', function() {
90 - var Component = React.createFactory(ComponentClass);
91 - var element;
92 -
93 - var Wrapper = React.createClass({
94 - childContextTypes: {
95 - foo: React.PropTypes.string
96 - },
97 - getChildContext: function() {
98 - return {foo: 'bar'};
99 - },
100 - render: function() {
101 - element = Component();
102 - return element;
103 - }
104 - });
105 -
106 - ReactTestUtils.renderIntoDocument(
107 - React.createElement(Wrapper)
108 - );
109 -
110 - expect(element._context).toEqual({foo: 'bar'});
111 - });
112 -
89 it('preserves the owner on the element', function() {
90 var Component = React.createFactory(ComponentClass);
91 var element;
src/core/ReactCompositeComponent.js
+2 -39
@@ -125,7 +125,7 @@ var ReactCompositeComponentMixin = {
125 this._rootNodeID = rootID;
126
127 var publicProps = this._processProps(this._currentElement.props);
128 - var publicContext = this._processContext(this._currentElement._context);
128 + var publicContext = this._processContext(context);
129
130 var Component = ReactNativeComponent.getComponentClassForElement(
131 this._currentElement
@@ -158,10 +158,6 @@ var ReactCompositeComponentMixin = {
158 // Store a reference from the instance back to the internal representation
159 ReactInstanceMap.set(inst, this);
160
161 - if (__DEV__) {
162 - this._warnIfContextsDiffer(this._currentElement._context, context);
163 - }
164 -
161 if (__DEV__) {
162 // Since plain JS classes are defined without any special initialization
163 // logic, we can not catch common errors early. Therefore, we have to
@@ -529,30 +525,6 @@ var ReactCompositeComponentMixin = {
525 }
526 },
527
532 - /**
533 - * Compare two contexts, warning if they are different
534 - * TODO: Remove this check when owner-context is removed
535 - */
536 - _warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) {
537 - ownerBasedContext = this._maskContext(ownerBasedContext);
538 - parentBasedContext = this._maskContext(parentBasedContext);
539 - var parentKeys = Object.keys(parentBasedContext).sort();
540 - var displayName = this.getName() || 'ReactCompositeComponent';
541 - for (var i = 0; i < parentKeys.length; i++) {
542 - var key = parentKeys[i];
543 - warning(
544 - ownerBasedContext[key] === parentBasedContext[key],
545 - 'owner-based and parent-based contexts differ ' +
546 - '(values: `%s` vs `%s`) for key (%s) while mounting %s ' +
547 - '(see: http://fb.me/react-context-by-parent)',
548 - ownerBasedContext[key],
549 - parentBasedContext[key],
550 - key,
551 - displayName
552 - );
553 - }
554 - },
555 -
528 /**
529 * Perform an update to a mounted component. The componentWillReceiveProps and
530 * shouldComponentUpdate methods are called, then (assuming the update isn't
@@ -582,18 +554,9 @@ var ReactCompositeComponentMixin = {
554
555 // Distinguish between a props update versus a simple state update
556 if (prevParentElement !== nextParentElement) {
585 - nextContext = this._processContext(nextParentElement._context);
557 + nextContext = this._processContext(nextUnmaskedContext);
558 nextProps = this._processProps(nextParentElement.props);
559
588 - if (__DEV__) {
589 - if (nextUnmaskedContext != null) {
590 - this._warnIfContextsDiffer(
591 - nextParentElement._context,
592 - nextUnmaskedContext
593 - );
594 - }
595 - }
596 -
560 // An update here will schedule an update but immediately set
561 // _pendingStateQueue which will ensure that any state updates gets
562 // immediately reconciled instead of waiting for the next batch.
src/core/ReactContext.js
+1 -43
@@ -11,11 +11,7 @@
11
12 'use strict';
13
14 -var assign = require('Object.assign');
14 var emptyObject = require('emptyObject');
16 -var warning = require('warning');
17 -
18 -var didWarn = false;
15
16 /**
17 * Keeps track of the current context.
@@ -29,45 +25,7 @@ var ReactContext = {
25 * @internal
26 * @type {object}
27 */
32 - current: emptyObject,
33 -
34 - /**
35 - * Temporarily extends the current context while executing scopedCallback.
36 - *
37 - * A typical use case might look like
38 - *
39 - * render: function() {
40 - * var children = ReactContext.withContext({foo: 'foo'}, () => (
41 - *
42 - * ));
43 - * return <div>{children}</div>;
44 - * }
45 - *
46 - * @param {object} newContext New context to merge into the existing context
47 - * @param {function} scopedCallback Callback to run with the new context
48 - * @return {ReactComponent|array<ReactComponent>}
49 - */
50 - withContext: function(newContext, scopedCallback) {
51 - if (__DEV__) {
52 - warning(
53 - didWarn,
54 - 'withContext is deprecated and will be removed in a future version. ' +
55 - 'Use a wrapper component with getChildContext instead.'
56 - );
57 -
58 - didWarn = true;
59 - }
60 -
61 - var result;
62 - var previousContext = ReactContext.current;
63 - ReactContext.current = assign({}, previousContext, newContext);
64 - try {
65 - result = scopedCallback();
66 - } finally {
67 - ReactContext.current = previousContext;
68 - }
69 - return result;
70 - }
28 + current: emptyObject
29
30 };
31
src/core/__tests__/ReactCompositeComponent-test.js
+33 -177
@@ -73,12 +73,7 @@ describe('ReactCompositeComponent', function() {
73 }
74 });
75
76 - // Ignore the first warning which is fired by using withContext at all.
77 - // That way we don't have to reset and assert it on every subsequent test.
78 - // This will be killed soon anyway.
76 console.error = mocks.getMockFunction();
80 - React.withContext({}, function() { });
81 -
77 spyOn(console, 'error');
78 });
79
@@ -621,178 +616,6 @@ describe('ReactCompositeComponent', function() {
616 reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', depth: 0});
617 });
618
624 - it('warn if context keys differ', function() {
625 - var Component = React.createClass({
626 - contextTypes: {
627 - foo: ReactPropTypes.string.isRequired
628 - },
629 -
630 - render: function() {
631 - return <div />;
632 - }
633 - });
634 -
635 - React.withContext({foo: 'bar'}, function() {
636 - ReactTestUtils.renderIntoDocument(<Component />);
637 - });
638 -
639 - expect(console.error.argsForCall.length).toBe(1);
640 - expect(console.error.argsForCall[0][0]).toBe(
641 - 'Warning: owner-based and parent-based contexts differ ' +
642 - '(values: `bar` vs `undefined`) for key (foo) ' +
643 - 'while mounting Component (see: http://fb.me/react-context-by-parent)'
644 - );
645 -
646 - });
647 -
648 - it('warn if context values differ', function() {
649 - var Parent = React.createClass({
650 - childContextTypes: {
651 - foo: ReactPropTypes.string
652 - },
653 -
654 - getChildContext: function() {
655 - return {
656 - foo: "bar"
657 - };
658 - },
659 -
660 - render: function() {
661 - return <div>{this.props.children}</div>;
662 - }
663 - });
664 - var Component = React.createClass({
665 - contextTypes: {
666 - foo: ReactPropTypes.string.isRequired
667 - },
668 -
669 - render: function() {
670 - return <div />;
671 - }
672 - });
673 -
674 - var component = React.withContext({foo: 'noise'}, function() {
675 - return <Component />;
676 - });
677 -
678 - ReactTestUtils.renderIntoDocument(<Parent>{component}</Parent>);
679 -
680 - // Two warnings, one for the component and one for the div
681 - // We may want to make this expect one warning in the future
682 - expect(console.error.argsForCall.length).toBe(1);
683 - expect(console.error.argsForCall[0][0]).toBe(
684 - 'Warning: owner-based and parent-based contexts differ ' +
685 - '(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
686 - '(see: http://fb.me/react-context-by-parent)'
687 - );
688 -
689 - });
690 -
691 - it('should warn if context values differ on update using withContext', function() {
692 - var Parent = React.createClass({
693 - childContextTypes: {
694 - foo: ReactPropTypes.string
695 - },
696 -
697 - getChildContext: function() {
698 - return {
699 - foo: "bar"
700 - };
701 - },
702 -
703 - render: function() {
704 - return <div>{this.props.children}</div>;
705 - }
706 - });
707 -
708 - var Component = React.createClass({
709 - contextTypes: {
710 - foo: ReactPropTypes.string.isRequired
711 - },
712 -
713 - render: function() {
714 - return <div />;
715 - }
716 - });
717 -
718 - var div = document.createElement('div');
719 -
720 - var componentWithSameContext = React.withContext({foo: 'bar'}, function() {
721 - return <Component />;
722 - });
723 - React.render(<Parent>{componentWithSameContext}</Parent>, div);
724 -
725 - expect(console.error.argsForCall.length).toBe(0);
726 -
727 - var componentWithDifferentContext = React.withContext({foo: 'noise'}, function() {
728 - return <Component />;
729 - });
730 - React.render(<Parent>{componentWithDifferentContext}</Parent>, div);
731 -
732 - // Two warnings, one for the component and one for the div
733 - // We may want to make this expect one warning in the future
734 - expect(console.error.argsForCall.length).toBe(1);
735 - expect(console.error.argsForCall[0][0]).toBe(
736 - 'Warning: owner-based and parent-based contexts differ ' +
737 - '(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
738 - '(see: http://fb.me/react-context-by-parent)'
739 - );
740 - });
741 -
742 - it('should warn if context values differ on update using wrapper', function() {
743 - var Parent = React.createClass({
744 - childContextTypes: {
745 - foo: ReactPropTypes.string
746 - },
747 -
748 - getChildContext: function() {
749 - return {
750 - foo: "bar"
751 - };
752 - },
753 -
754 - render: function() {
755 - return <div>{this.props.children}</div>;
756 - }
757 - });
758 -
759 - var Component = React.createClass({
760 - contextTypes: {
761 - foo: ReactPropTypes.string.isRequired
762 - },
763 -
764 - render: function() {
765 - return <div />;
766 - }
767 - });
768 -
769 - var Wrapper = React.createClass({
770 - childContextTypes: {
771 - foo: ReactPropTypes.string
772 - },
773 -
774 - getChildContext: function() {
775 - return {foo: this.props.foo};
776 - },
777 -
778 - render: function() { return <Parent><Component /></Parent>; }
779 -
780 - });
781 -
782 - var div = document.createElement('div');
783 - React.render(<Wrapper foo='bar' />, div);
784 - React.render(<Wrapper foo='noise' />, div);
785 -
786 - // Two warnings, one for the component and one for the div
787 - // We may want to make this expect one warning in the future
788 - expect(console.error.argsForCall.length).toBe(1);
789 - expect(console.error.argsForCall[0][0]).toBe(
790 - 'Warning: owner-based and parent-based contexts differ ' +
791 - '(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
792 - '(see: http://fb.me/react-context-by-parent)'
793 - );
794 - });
795 -
619 it('unmasked context propagates through updates', function() {
620
621 var Leaf = React.createClass({
@@ -996,4 +819,37 @@ describe('ReactCompositeComponent', function() {
819 );
820 });
821
822 + it('context should be passed down from the parent', function() {
823 + var Parent = React.createClass({
824 + childContextTypes: {
825 + foo: ReactPropTypes.string
826 + },
827 +
828 + getChildContext: function() {
829 + return {
830 + foo: "bar"
831 + };
832 + },
833 +
834 + render: function() {
835 + return <div>{this.props.children}</div>;
836 + }
837 + });
838 +
839 + var Component = React.createClass({
840 + contextTypes: {
841 + foo: ReactPropTypes.string.isRequired
842 + },
843 +
844 + render: function() {
845 + return <div />;
846 + }
847 + });
848 +
849 + var div = document.createElement('div');
850 + React.render(<Parent><Component /></Parent>, div);
851 +
852 + expect(console.error.argsForCall.length).toBe(0);
853 + });
854 +
855 });