Skip to content

Commit aaeb3d4

Browse files
committed
Revert "Switch to parent-based context. Fixes facebook#2112."
This reverts commit 7d44917.
1 parent f6ae856 commit aaeb3d4

File tree

5 files changed

+287
-36
lines changed

5 files changed

+287
-36
lines changed

src/classic/element/ReactElement.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ var ReactElement = function(type, key, ref, owner, context, props) {
9999
// Record the component responsible for creating this element.
100100
this._owner = owner;
101101

102+
// TODO: Deprecate withContext, and then the context becomes accessible
103+
// through the owner.
104+
this._context = context;
105+
102106
if (__DEV__) {
103107
// The validation flag and props are currently mutative. We put them on
104108
// an external backing store so that we can freeze the whole object.

src/classic/element/__tests__/ReactElement-test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,30 @@ describe('ReactElement', function() {
8686
expect(element.props).toEqual({foo:'56'});
8787
});
8888

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+
89113
it('preserves the owner on the element', function() {
90114
var Component = React.createFactory(ComponentClass);
91115
var element;

src/core/ReactCompositeComponent.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ var ReactCompositeComponentMixin = {
125125
this._rootNodeID = rootID;
126126

127127
var publicProps = this._processProps(this._currentElement.props);
128-
var publicContext = this._processContext(context);
128+
var publicContext = this._processContext(this._currentElement._context);
129129

130130
var Component = ReactNativeComponent.getComponentClassForElement(
131131
this._currentElement
@@ -158,6 +158,10 @@ var ReactCompositeComponentMixin = {
158158
// Store a reference from the instance back to the internal representation
159159
ReactInstanceMap.set(inst, this);
160160

161+
if (__DEV__) {
162+
this._warnIfContextsDiffer(this._currentElement._context, context);
163+
}
164+
161165
if (__DEV__) {
162166
// Since plain JS classes are defined without any special initialization
163167
// logic, we can not catch common errors early. Therefore, we have to
@@ -533,6 +537,30 @@ var ReactCompositeComponentMixin = {
533537
}
534538
},
535539

540+
/**
541+
* Compare two contexts, warning if they are different
542+
* TODO: Remove this check when owner-context is removed
543+
*/
544+
_warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) {
545+
ownerBasedContext = this._maskContext(ownerBasedContext);
546+
parentBasedContext = this._maskContext(parentBasedContext);
547+
var parentKeys = Object.keys(parentBasedContext).sort();
548+
var displayName = this.getName() || 'ReactCompositeComponent';
549+
for (var i = 0; i < parentKeys.length; i++) {
550+
var key = parentKeys[i];
551+
warning(
552+
ownerBasedContext[key] === parentBasedContext[key],
553+
'owner-based and parent-based contexts differ ' +
554+
'(values: `%s` vs `%s`) for key (%s) while mounting %s ' +
555+
'(see: http://fb.me/react-context-by-parent)',
556+
ownerBasedContext[key],
557+
parentBasedContext[key],
558+
key,
559+
displayName
560+
);
561+
}
562+
},
563+
536564
/**
537565
* Perform an update to a mounted component. The componentWillReceiveProps and
538566
* shouldComponentUpdate methods are called, then (assuming the update isn't
@@ -562,9 +590,18 @@ var ReactCompositeComponentMixin = {
562590

563591
// Distinguish between a props update versus a simple state update
564592
if (prevParentElement !== nextParentElement) {
565-
nextContext = this._processContext(nextUnmaskedContext);
593+
nextContext = this._processContext(nextParentElement._context);
566594
nextProps = this._processProps(nextParentElement.props);
567595

596+
if (__DEV__) {
597+
if (nextUnmaskedContext != null) {
598+
this._warnIfContextsDiffer(
599+
nextParentElement._context,
600+
nextUnmaskedContext
601+
);
602+
}
603+
}
604+
568605
// An update here will schedule an update but immediately set
569606
// _pendingStateQueue which will ensure that any state updates gets
570607
// immediately reconciled instead of waiting for the next batch.

src/core/ReactContext.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111

1212
'use strict';
1313

14+
var assign = require('Object.assign');
1415
var emptyObject = require('emptyObject');
16+
var warning = require('warning');
17+
18+
var didWarn = false;
1519

1620
/**
1721
* Keeps track of the current context.
@@ -25,7 +29,45 @@ var ReactContext = {
2529
* @internal
2630
* @type {object}
2731
*/
28-
current: emptyObject
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+
}
2971

3072
};
3173

0 commit comments

Comments
 (0)