mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Updated ReactCompositeComponent to trigger componentWillReceiveProps when either props or context change. Fixes issue #5756
This commit is contained in:
@@ -647,12 +647,18 @@ var ReactCompositeComponentMixin = {
|
||||
nextUnmaskedContext
|
||||
) {
|
||||
var inst = this._instance;
|
||||
|
||||
var nextContext = this._context === nextUnmaskedContext ?
|
||||
inst.context :
|
||||
this._processContext(nextUnmaskedContext);
|
||||
var willReceive = false;
|
||||
var nextContext;
|
||||
var nextProps;
|
||||
|
||||
// Determine if the context has changed or not
|
||||
if (this._context === nextUnmaskedContext) {
|
||||
nextContext = inst.context;
|
||||
} else {
|
||||
nextContext = this._processContext(nextUnmaskedContext);
|
||||
willReceive = true;
|
||||
}
|
||||
|
||||
// Distinguish between a props update versus a simple state update
|
||||
if (prevParentElement === nextParentElement) {
|
||||
// Skip checking prop types again -- we don't read inst.props to avoid
|
||||
@@ -660,13 +666,14 @@ var ReactCompositeComponentMixin = {
|
||||
nextProps = nextParentElement.props;
|
||||
} else {
|
||||
nextProps = this._processProps(nextParentElement.props);
|
||||
// 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.
|
||||
willReceive = true;
|
||||
}
|
||||
|
||||
if (inst.componentWillReceiveProps) {
|
||||
inst.componentWillReceiveProps(nextProps, nextContext);
|
||||
}
|
||||
// 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.
|
||||
if (willReceive && inst.componentWillReceiveProps) {
|
||||
inst.componentWillReceiveProps(nextProps, nextContext);
|
||||
}
|
||||
|
||||
var nextState = this._processPendingState(nextProps, nextContext);
|
||||
|
||||
@@ -874,6 +874,123 @@ describe('ReactCompositeComponent', function() {
|
||||
expect(div.children[0].id).toBe('aliens');
|
||||
});
|
||||
|
||||
it('should trigger componentWillReceiveProps for context changes', function() {
|
||||
var contextChanges = 0;
|
||||
var propChanges = 0;
|
||||
|
||||
var GrandChild = React.createClass({
|
||||
contextTypes: {
|
||||
foo: ReactPropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <span className="grand-child">{this.props.children}</span>;
|
||||
},
|
||||
});
|
||||
|
||||
var ChildWithContext = React.createClass({
|
||||
contextTypes: {
|
||||
foo: ReactPropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div className="child-with">{this.props.children}</div>;
|
||||
},
|
||||
});
|
||||
|
||||
var ChildWithoutContext = React.createClass({
|
||||
componentWillReceiveProps: function(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div className="child-without">{this.props.children}</div>;
|
||||
},
|
||||
});
|
||||
|
||||
var Parent = React.createClass({
|
||||
childContextTypes: {
|
||||
foo: ReactPropTypes.string,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
foo: 'abc',
|
||||
};
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
foo: this.state.foo,
|
||||
};
|
||||
},
|
||||
|
||||
onClick() {
|
||||
this.setState({
|
||||
foo: 'def',
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div className="parent" onClick={this.onClick}>{this.props.children}</div>;
|
||||
},
|
||||
});
|
||||
|
||||
var div = document.createElement('div');
|
||||
|
||||
ReactDOM.render(
|
||||
<Parent>
|
||||
<ChildWithoutContext>
|
||||
A1
|
||||
<GrandChild>A2</GrandChild>
|
||||
</ChildWithoutContext>
|
||||
|
||||
<ChildWithContext>
|
||||
B1
|
||||
<GrandChild>B2</GrandChild>
|
||||
</ChildWithContext>
|
||||
</Parent>,
|
||||
div
|
||||
);
|
||||
|
||||
ReactTestUtils.Simulate.click(div.childNodes[0]);
|
||||
|
||||
expect(propChanges).toBe(0);
|
||||
expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2
|
||||
});
|
||||
|
||||
it('should disallow nested render calls', function() {
|
||||
var Inner = React.createClass({
|
||||
render: function() {
|
||||
|
||||
Reference in New Issue
Block a user