diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js
index f2f321426a..df365ea911 100644
--- a/src/core/ReactCompositeComponent.js
+++ b/src/core/ReactCompositeComponent.js
@@ -723,6 +723,12 @@ var ReactCompositeComponentMixin = assign({},
nextContext = this._processContext(nextParentElement._context);
nextProps = this._processProps(nextParentElement.props);
+ if (__DEV__) {
+ if (nextUnmaskedContext != null) {
+ this._warnIfContextsDiffer(nextParentElement._context, nextUnmaskedContext);
+ }
+ }
+
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
if (inst.componentWillReceiveProps) {
inst.componentWillReceiveProps(nextProps, nextContext);
@@ -808,7 +814,7 @@ var ReactCompositeComponentMixin = assign({},
inst.state = nextState;
inst.context = nextContext;
- this._updateRenderedComponent(transaction, nextContext);
+ this._updateRenderedComponent(transaction, unmaskedContext);
if (inst.componentDidUpdate) {
transaction.getReactMountReady().enqueue(
diff --git a/src/core/__tests__/ReactCompositeComponent-test.js b/src/core/__tests__/ReactCompositeComponent-test.js
index 5122601b10..4cb128842d 100644
--- a/src/core/__tests__/ReactCompositeComponent-test.js
+++ b/src/core/__tests__/ReactCompositeComponent-test.js
@@ -579,7 +579,14 @@ describe('ReactCompositeComponent', function() {
ReactTestUtils.renderIntoDocument();
});
+ // Two warnings, one for the component and one for the div
+ // We may want to make this expect one warning in the future
expect(console.warn.mock.calls.length).toBe(2);
+ expect(console.warn.mock.calls[0][0]).toBe(
+ 'Warning: owner based context (keys: foo) does not equal parent based ' +
+ 'context (keys: ) while mounting Component ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
expect(console.warn.mock.calls[1][0]).toBe(
'Warning: owner based context (keys: foo) does not equal parent based ' +
'context (keys: ) while mounting ReactCompositeComponent ' +
@@ -620,15 +627,212 @@ describe('ReactCompositeComponent', function() {
ReactTestUtils.renderIntoDocument({component});
+ // Two warnings, one for the component and one for the div
+ // We may want to make this expect one warning in the future
expect(console.warn.mock.calls.length).toBe(2);
expect(console.warn.mock.calls[0][0]).toBe(
'Warning: owner-based and parent-based contexts differ ' +
'(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
'(see: http://fb.me/react-context-by-parent)'
);
+ expect(console.warn.mock.calls[1][0]).toBe(
+ 'Warning: owner-based and parent-based contexts differ ' +
+ '(values: `noise` vs `bar`) for key (foo) while mounting ReactCompositeComponent ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
});
+ it('should warn if context values differ on update using withContext', function() {
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: ReactPropTypes.string
+ },
+
+ getChildContext: function() {
+ return {
+ foo: "bar"
+ };
+ },
+
+ render: function() {
+ return
{this.props.children}
;
+ }
+ });
+
+ var Component = React.createClass({
+ contextTypes: {
+ foo: ReactPropTypes.string.isRequired
+ },
+
+ render: function() {
+ return ;
+ }
+ });
+
+ var div = document.createElement('div');
+
+ var componentWithSameContext = React.withContext({foo: 'bar'}, function() {
+ return ;
+ });
+ React.render({componentWithSameContext}, div);
+
+ expect(console.warn.mock.calls.length).toBe(0);
+
+ var componentWithDifferentContext = React.withContext({foo: 'noise'}, function() {
+ return ;
+ });
+ React.render({componentWithDifferentContext}, div);
+
+ // Two warnings, one for the component and one for the div
+ // We may want to make this expect one warning in the future
+ expect(console.warn.mock.calls.length).toBe(2);
+ expect(console.warn.mock.calls[0][0]).toBe(
+ 'Warning: owner-based and parent-based contexts differ ' +
+ '(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
+ expect(console.warn.mock.calls[1][0]).toBe(
+ 'Warning: owner-based and parent-based contexts differ ' +
+ '(values: `noise` vs `bar`) for key (foo) while mounting ReactCompositeComponent ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
+
+ });
+
+ it('should warn if context values differ on update using wrapper', function() {
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: ReactPropTypes.string
+ },
+
+ getChildContext: function() {
+ return {
+ foo: "bar"
+ };
+ },
+
+ render: function() {
+ return {this.props.children}
;
+ }
+ });
+
+ var Component = React.createClass({
+ contextTypes: {
+ foo: ReactPropTypes.string.isRequired
+ },
+
+ render: function() {
+ return ;
+ }
+ });
+
+ var Wrapper = React.createClass({
+ childContextTypes: {
+ foo: ReactPropTypes.string
+ },
+
+ getChildContext: function() {
+ return { foo: this.props.foo };
+ },
+
+ render: function() { return ; }
+
+ });
+
+ var div = document.createElement('div');
+ React.render(, div);
+ React.render(, div);
+
+ // Two warnings, one for the component and one for the div
+ // We may want to make this expect one warning in the future
+ expect(console.warn.mock.calls.length).toBe(2);
+ expect(console.warn.mock.calls[0][0]).toBe(
+ 'Warning: owner-based and parent-based contexts differ ' +
+ '(values: `noise` vs `bar`) for key (foo) while mounting Component ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
+ expect(console.warn.mock.calls[1][0]).toBe(
+ 'Warning: owner-based and parent-based contexts differ ' +
+ '(values: `noise` vs `bar`) for key (foo) while mounting ReactCompositeComponent ' +
+ '(see: http://fb.me/react-context-by-parent)'
+ );
+
+ });
+
+ it('unmasked context propagates through updates', function() {
+
+ var Leaf = React.createClass({
+ contextTypes: {
+ foo: ReactPropTypes.string.isRequired
+ },
+
+ componentWillReceiveProps: function(nextProps, nextContext) {
+ expect('foo' in nextContext).toBe(true);
+ },
+
+ componentDidUpdate: function(prevProps, prevState, prevContext) {
+ expect('foo' in prevContext).toBe(true);
+ },
+
+ shouldComponentUpdate: function(nextProps, nextState, nextContext) {
+ expect('foo' in nextContext).toBe(true);
+ return true;
+ },
+
+ render: function() {
+ return {this.context.foo};
+ }
+ });
+
+ var Intermediary = React.createClass({
+
+ componentWillReceiveProps: function(nextProps, nextContext) {
+ expect('foo' in nextContext).toBe(false);
+ },
+
+ componentDidUpdate: function(prevProps, prevState, prevContext) {
+ expect('foo' in prevContext).toBe(false);
+ },
+
+ shouldComponentUpdate: function(nextProps, nextState, nextContext) {
+ expect('foo' in nextContext).toBe(false);
+ return true;
+ },
+
+ render: function() {
+ return ;
+ }
+ });
+
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: ReactPropTypes.string
+ },
+
+ getChildContext: function() {
+ return {
+ foo: this.props.cntxt
+ };
+ },
+
+ render: function() {
+ return ;
+ }
+ });
+
+ var div = document.createElement('div');
+ React.render(, div);
+ expect(div.children[0].innerHTML).toBe('noise');
+ div.children[0].innerHTML = 'aliens';
+ div.children[0].id = 'aliens';
+ expect(div.children[0].innerHTML).toBe('aliens');
+ expect(div.children[0].id).toBe('aliens');
+ React.render(, div);
+ expect(div.children[0].innerHTML).toBe('bar');
+ expect(div.children[0].id).toBe('aliens');
+ });
+
it('should disallow nested render calls', function() {
spyOn(console, 'warn');
var Inner = React.createClass({