mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 12:20:38 +01:00
Merge pull request #3615 from jsfb/enable-new-context
Switch to parent-based context. Fixes #2112.
This commit is contained in:
commit
0185c68c91
|
|
@ -99,10 +99,6 @@ var ReactElement = function(type, key, ref, owner, context, props) {
|
||||||
// Record the component responsible for creating this element.
|
// Record the component responsible for creating this element.
|
||||||
this._owner = owner;
|
this._owner = owner;
|
||||||
|
|
||||||
// TODO: Deprecate withContext, and then the context becomes accessible
|
|
||||||
// through the owner.
|
|
||||||
this._context = context;
|
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
// The validation flag and props are currently mutative. We put them on
|
// The validation flag and props are currently mutative. We put them on
|
||||||
// an external backing store so that we can freeze the whole object.
|
// an external backing store so that we can freeze the whole object.
|
||||||
|
|
|
||||||
|
|
@ -86,30 +86,6 @@ describe('ReactElement', function() {
|
||||||
expect(element.props).toEqual({foo:'56'});
|
expect(element.props).toEqual({foo:'56'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves the legacy context on the element', function() {
|
|
||||||
var Component = React.createFactory(ComponentClass);
|
|
||||||
var element;
|
|
||||||
|
|
||||||
var Wrapper = React.createClass({
|
|
||||||
childContextTypes: {
|
|
||||||
foo: React.PropTypes.string
|
|
||||||
},
|
|
||||||
getChildContext: function() {
|
|
||||||
return {foo: 'bar'};
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
element = Component();
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ReactTestUtils.renderIntoDocument(
|
|
||||||
React.createElement(Wrapper)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(element._context).toEqual({foo: 'bar'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('preserves the owner on the element', function() {
|
it('preserves the owner on the element', function() {
|
||||||
var Component = React.createFactory(ComponentClass);
|
var Component = React.createFactory(ComponentClass);
|
||||||
var element;
|
var element;
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ var ReactCompositeComponentMixin = {
|
||||||
this._rootNodeID = rootID;
|
this._rootNodeID = rootID;
|
||||||
|
|
||||||
var publicProps = this._processProps(this._currentElement.props);
|
var publicProps = this._processProps(this._currentElement.props);
|
||||||
var publicContext = this._processContext(this._currentElement._context);
|
var publicContext = this._processContext(context);
|
||||||
|
|
||||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||||
this._currentElement
|
this._currentElement
|
||||||
|
|
@ -158,10 +158,6 @@ var ReactCompositeComponentMixin = {
|
||||||
// Store a reference from the instance back to the internal representation
|
// Store a reference from the instance back to the internal representation
|
||||||
ReactInstanceMap.set(inst, this);
|
ReactInstanceMap.set(inst, this);
|
||||||
|
|
||||||
if (__DEV__) {
|
|
||||||
this._warnIfContextsDiffer(this._currentElement._context, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
// Since plain JS classes are defined without any special initialization
|
// Since plain JS classes are defined without any special initialization
|
||||||
// logic, we can not catch common errors early. Therefore, we have to
|
// logic, we can not catch common errors early. Therefore, we have to
|
||||||
|
|
@ -529,30 +525,6 @@ var ReactCompositeComponentMixin = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two contexts, warning if they are different
|
|
||||||
* TODO: Remove this check when owner-context is removed
|
|
||||||
*/
|
|
||||||
_warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) {
|
|
||||||
ownerBasedContext = this._maskContext(ownerBasedContext);
|
|
||||||
parentBasedContext = this._maskContext(parentBasedContext);
|
|
||||||
var parentKeys = Object.keys(parentBasedContext).sort();
|
|
||||||
var displayName = this.getName() || 'ReactCompositeComponent';
|
|
||||||
for (var i = 0; i < parentKeys.length; i++) {
|
|
||||||
var key = parentKeys[i];
|
|
||||||
warning(
|
|
||||||
ownerBasedContext[key] === parentBasedContext[key],
|
|
||||||
'owner-based and parent-based contexts differ ' +
|
|
||||||
'(values: `%s` vs `%s`) for key (%s) while mounting %s ' +
|
|
||||||
'(see: http://fb.me/react-context-by-parent)',
|
|
||||||
ownerBasedContext[key],
|
|
||||||
parentBasedContext[key],
|
|
||||||
key,
|
|
||||||
displayName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an update to a mounted component. The componentWillReceiveProps and
|
* Perform an update to a mounted component. The componentWillReceiveProps and
|
||||||
* shouldComponentUpdate methods are called, then (assuming the update isn't
|
* shouldComponentUpdate methods are called, then (assuming the update isn't
|
||||||
|
|
@ -582,18 +554,9 @@ var ReactCompositeComponentMixin = {
|
||||||
|
|
||||||
// Distinguish between a props update versus a simple state update
|
// Distinguish between a props update versus a simple state update
|
||||||
if (prevParentElement !== nextParentElement) {
|
if (prevParentElement !== nextParentElement) {
|
||||||
nextContext = this._processContext(nextParentElement._context);
|
nextContext = this._processContext(nextUnmaskedContext);
|
||||||
nextProps = this._processProps(nextParentElement.props);
|
nextProps = this._processProps(nextParentElement.props);
|
||||||
|
|
||||||
if (__DEV__) {
|
|
||||||
if (nextUnmaskedContext != null) {
|
|
||||||
this._warnIfContextsDiffer(
|
|
||||||
nextParentElement._context,
|
|
||||||
nextUnmaskedContext
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An update here will schedule an update but immediately set
|
// An update here will schedule an update but immediately set
|
||||||
// _pendingStateQueue which will ensure that any state updates gets
|
// _pendingStateQueue which will ensure that any state updates gets
|
||||||
// immediately reconciled instead of waiting for the next batch.
|
// immediately reconciled instead of waiting for the next batch.
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,7 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var assign = require('Object.assign');
|
|
||||||
var emptyObject = require('emptyObject');
|
var emptyObject = require('emptyObject');
|
||||||
var warning = require('warning');
|
|
||||||
|
|
||||||
var didWarn = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of the current context.
|
* Keeps track of the current context.
|
||||||
|
|
@ -29,45 +25,7 @@ var ReactContext = {
|
||||||
* @internal
|
* @internal
|
||||||
* @type {object}
|
* @type {object}
|
||||||
*/
|
*/
|
||||||
current: emptyObject,
|
current: emptyObject
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporarily extends the current context while executing scopedCallback.
|
|
||||||
*
|
|
||||||
* A typical use case might look like
|
|
||||||
*
|
|
||||||
* render: function() {
|
|
||||||
* var children = ReactContext.withContext({foo: 'foo'}, () => (
|
|
||||||
*
|
|
||||||
* ));
|
|
||||||
* return <div>{children}</div>;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param {object} newContext New context to merge into the existing context
|
|
||||||
* @param {function} scopedCallback Callback to run with the new context
|
|
||||||
* @return {ReactComponent|array<ReactComponent>}
|
|
||||||
*/
|
|
||||||
withContext: function(newContext, scopedCallback) {
|
|
||||||
if (__DEV__) {
|
|
||||||
warning(
|
|
||||||
didWarn,
|
|
||||||
'withContext is deprecated and will be removed in a future version. ' +
|
|
||||||
'Use a wrapper component with getChildContext instead.'
|
|
||||||
);
|
|
||||||
|
|
||||||
didWarn = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result;
|
|
||||||
var previousContext = ReactContext.current;
|
|
||||||
ReactContext.current = assign({}, previousContext, newContext);
|
|
||||||
try {
|
|
||||||
result = scopedCallback();
|
|
||||||
} finally {
|
|
||||||
ReactContext.current = previousContext;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@ describe('ReactCompositeComponent', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ignore the first warning which is fired by using withContext at all.
|
|
||||||
// That way we don't have to reset and assert it on every subsequent test.
|
|
||||||
// This will be killed soon anyway.
|
|
||||||
console.error = mocks.getMockFunction();
|
console.error = mocks.getMockFunction();
|
||||||
React.withContext({}, function() { });
|
|
||||||
|
|
||||||
spyOn(console, 'error');
|
spyOn(console, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -609,178 +604,6 @@ describe('ReactCompositeComponent', function() {
|
||||||
reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', depth: 0});
|
reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', depth: 0});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('warn if context keys differ', function() {
|
|
||||||
var Component = React.createClass({
|
|
||||||
contextTypes: {
|
|
||||||
foo: ReactPropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
React.withContext({foo: 'bar'}, function() {
|
|
||||||
ReactTestUtils.renderIntoDocument(<Component />);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(console.error.argsForCall.length).toBe(1);
|
|
||||||
expect(console.error.argsForCall[0][0]).toBe(
|
|
||||||
'Warning: owner-based and parent-based contexts differ ' +
|
|
||||||
'(values: `bar` vs `undefined`) for key (foo) ' +
|
|
||||||
'while mounting Component (see: http://fb.me/react-context-by-parent)'
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('warn if context values differ', function() {
|
|
||||||
var Parent = React.createClass({
|
|
||||||
childContextTypes: {
|
|
||||||
foo: ReactPropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getChildContext: function() {
|
|
||||||
return {
|
|
||||||
foo: "bar"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return <div>{this.props.children}</div>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var Component = React.createClass({
|
|
||||||
contextTypes: {
|
|
||||||
foo: ReactPropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var component = React.withContext({foo: 'noise'}, function() {
|
|
||||||
return <Component />;
|
|
||||||
});
|
|
||||||
|
|
||||||
ReactTestUtils.renderIntoDocument(<Parent>{component}</Parent>);
|
|
||||||
|
|
||||||
// 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.error.argsForCall.length).toBe(1);
|
|
||||||
expect(console.error.argsForCall[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)'
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
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 <div>{this.props.children}</div>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Component = React.createClass({
|
|
||||||
contextTypes: {
|
|
||||||
foo: ReactPropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var div = document.createElement('div');
|
|
||||||
|
|
||||||
var componentWithSameContext = React.withContext({foo: 'bar'}, function() {
|
|
||||||
return <Component />;
|
|
||||||
});
|
|
||||||
React.render(<Parent>{componentWithSameContext}</Parent>, div);
|
|
||||||
|
|
||||||
expect(console.error.argsForCall.length).toBe(0);
|
|
||||||
|
|
||||||
var componentWithDifferentContext = React.withContext({foo: 'noise'}, function() {
|
|
||||||
return <Component />;
|
|
||||||
});
|
|
||||||
React.render(<Parent>{componentWithDifferentContext}</Parent>, 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.error.argsForCall.length).toBe(1);
|
|
||||||
expect(console.error.argsForCall[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)'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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 <div>{this.props.children}</div>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Component = React.createClass({
|
|
||||||
contextTypes: {
|
|
||||||
foo: ReactPropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Wrapper = React.createClass({
|
|
||||||
childContextTypes: {
|
|
||||||
foo: ReactPropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getChildContext: function() {
|
|
||||||
return {foo: this.props.foo};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() { return <Parent><Component /></Parent>; }
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
var div = document.createElement('div');
|
|
||||||
React.render(<Wrapper foo='bar' />, div);
|
|
||||||
React.render(<Wrapper foo='noise' />, 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.error.argsForCall.length).toBe(1);
|
|
||||||
expect(console.error.argsForCall[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)'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unmasked context propagates through updates', function() {
|
it('unmasked context propagates through updates', function() {
|
||||||
|
|
||||||
var Leaf = React.createClass({
|
var Leaf = React.createClass({
|
||||||
|
|
@ -984,4 +807,37 @@ describe('ReactCompositeComponent', function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('context should be passed down from the parent', function() {
|
||||||
|
var Parent = React.createClass({
|
||||||
|
childContextTypes: {
|
||||||
|
foo: ReactPropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getChildContext: function() {
|
||||||
|
return {
|
||||||
|
foo: "bar"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return <div>{this.props.children}</div>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Component = React.createClass({
|
||||||
|
contextTypes: {
|
||||||
|
foo: ReactPropTypes.string.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var div = document.createElement('div');
|
||||||
|
React.render(<Parent><Component /></Parent>, div);
|
||||||
|
|
||||||
|
expect(console.error.argsForCall.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user