mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 00:20:28 +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.
|
||||
this._owner = owner;
|
||||
|
||||
// TODO: Deprecate withContext, and then the context becomes accessible
|
||||
// through the owner.
|
||||
this._context = context;
|
||||
|
||||
if (__DEV__) {
|
||||
// The validation flag and props are currently mutative. We put them on
|
||||
// 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'});
|
||||
});
|
||||
|
||||
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() {
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
var element;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ var ReactCompositeComponentMixin = {
|
|||
this._rootNodeID = rootID;
|
||||
|
||||
var publicProps = this._processProps(this._currentElement.props);
|
||||
var publicContext = this._processContext(this._currentElement._context);
|
||||
var publicContext = this._processContext(context);
|
||||
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
|
|
@ -158,10 +158,6 @@ var ReactCompositeComponentMixin = {
|
|||
// Store a reference from the instance back to the internal representation
|
||||
ReactInstanceMap.set(inst, this);
|
||||
|
||||
if (__DEV__) {
|
||||
this._warnIfContextsDiffer(this._currentElement._context, context);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// Since plain JS classes are defined without any special initialization
|
||||
// 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
|
||||
* 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
|
||||
if (prevParentElement !== nextParentElement) {
|
||||
nextContext = this._processContext(nextParentElement._context);
|
||||
nextContext = this._processContext(nextUnmaskedContext);
|
||||
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
|
||||
// _pendingStateQueue which will ensure that any state updates gets
|
||||
// immediately reconciled instead of waiting for the next batch.
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var emptyObject = require('emptyObject');
|
||||
var warning = require('warning');
|
||||
|
||||
var didWarn = false;
|
||||
|
||||
/**
|
||||
* Keeps track of the current context.
|
||||
|
|
@ -29,45 +25,7 @@ var ReactContext = {
|
|||
* @internal
|
||||
* @type {object}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
current: emptyObject
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
React.withContext({}, function() { });
|
||||
|
||||
spyOn(console, 'error');
|
||||
});
|
||||
|
||||
|
|
@ -609,178 +604,6 @@ describe('ReactCompositeComponent', function() {
|
|||
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() {
|
||||
|
||||
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