diff --git a/src/addons/__tests__/renderSubtreeIntoContainer-test.js b/src/addons/__tests__/renderSubtreeIntoContainer-test.js
index 7898af3d7fba8..dcb70481a7fe6 100644
--- a/src/addons/__tests__/renderSubtreeIntoContainer-test.js
+++ b/src/addons/__tests__/renderSubtreeIntoContainer-test.js
@@ -12,13 +12,13 @@
'use strict';
var React = require('React');
+var ReactDOM = require('ReactDOM');
var ReactTestUtils = require('ReactTestUtils');
var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
describe('renderSubtreeIntoContainer', function() {
it('should pass context when rendering subtree elsewhere', function() {
-
var portal = document.createElement('div');
var Component = React.createClass({
@@ -92,4 +92,107 @@ describe('renderSubtreeIntoContainer', function() {
},
});
});
+
+ it('should update context if it changes due to setState', function() {
+ var container = document.createElement('div');
+ document.body.appendChild(container);
+ var portal = document.createElement('div');
+
+ var Component = React.createClass({
+ contextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ getFoo: React.PropTypes.func.isRequired,
+ },
+
+ render: function() {
+ return
{this.context.foo + '-' + this.context.getFoo()}
;
+ },
+ });
+
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ getFoo: React.PropTypes.func.isRequired,
+ },
+
+ getChildContext: function() {
+ return {
+ foo: this.state.bar,
+ getFoo: () => this.state.bar,
+ };
+ },
+
+ getInitialState: function() {
+ return {
+ bar: 'initial',
+ };
+ },
+
+ render: function() {
+ return null;
+ },
+
+ componentDidMount: function() {
+ renderSubtreeIntoContainer(this, , portal);
+ },
+
+ componentDidUpdate() {
+ renderSubtreeIntoContainer(this, , portal);
+ },
+ });
+
+ var instance = ReactDOM.render(, container);
+ expect(portal.firstChild.innerHTML).toBe('initial-initial');
+ instance.setState({bar: 'changed'});
+ expect(portal.firstChild.innerHTML).toBe('changed-changed');
+ });
+
+ it('should update context if it changes due to re-render', function() {
+ var container = document.createElement('div');
+ document.body.appendChild(container);
+ var portal = document.createElement('div');
+
+ var Component = React.createClass({
+ contextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ getFoo: React.PropTypes.func.isRequired,
+ },
+
+ render: function() {
+ return {this.context.foo + '-' + this.context.getFoo()}
;
+ },
+ });
+
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ getFoo: React.PropTypes.func.isRequired,
+ },
+
+ getChildContext: function() {
+ return {
+ foo: this.props.bar,
+ getFoo: () => this.props.bar,
+ };
+ },
+
+ render: function() {
+ return null;
+ },
+
+ componentDidMount: function() {
+ renderSubtreeIntoContainer(this, , portal);
+ },
+
+ componentDidUpdate() {
+ renderSubtreeIntoContainer(this, , portal);
+ },
+ });
+
+ ReactDOM.render(, container);
+ expect(portal.firstChild.innerHTML).toBe('initial-initial');
+ ReactDOM.render(, container);
+ expect(portal.firstChild.innerHTML).toBe('changed-changed');
+ });
+
});
diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js
index 5e1827b746519..d7fb1b561f835 100644
--- a/src/renderers/dom/client/ReactMount.js
+++ b/src/renderers/dom/client/ReactMount.js
@@ -20,6 +20,7 @@ var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactElement = require('ReactElement');
var ReactFeatureFlags = require('ReactFeatureFlags');
+var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactReconciler = require('ReactReconciler');
@@ -287,10 +288,11 @@ var ReactMount = {
_updateRootComponent: function(
prevComponent,
nextElement,
+ nextContext,
container,
callback) {
ReactMount.scrollMonitor(container, function() {
- ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement);
+ ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement, nextContext);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
@@ -389,7 +391,7 @@ var ReactMount = {
*/
renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
invariant(
- parentComponent != null && parentComponent._reactInternalInstance != null,
+ parentComponent != null && ReactInstanceMap.has(parentComponent),
'parentComponent must be a valid React Component'
);
return ReactMount._renderSubtreeIntoContainer(
@@ -440,6 +442,14 @@ var ReactMount = {
nextElement
);
+ var nextContext;
+ if (parentComponent) {
+ var parentInst = ReactInstanceMap.get(parentComponent);
+ nextContext = parentInst._processChildContext(parentInst._context);
+ } else {
+ nextContext = emptyObject;
+ }
+
var prevComponent = getTopLevelWrapperInContainer(container);
if (prevComponent) {
@@ -453,6 +463,7 @@ var ReactMount = {
ReactMount._updateRootComponent(
prevComponent,
nextWrappedElement,
+ nextContext,
container,
updatedCallback
);
@@ -501,11 +512,7 @@ var ReactMount = {
nextWrappedElement,
container,
shouldReuseMarkup,
- parentComponent != null ?
- parentComponent._reactInternalInstance._processChildContext(
- parentComponent._reactInternalInstance._context
- ) :
- emptyObject
+ nextContext
)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
diff --git a/src/renderers/native/ReactNativeMount.js b/src/renderers/native/ReactNativeMount.js
index 8fc575d97f930..d9fe2f28623fe 100644
--- a/src/renderers/native/ReactNativeMount.js
+++ b/src/renderers/native/ReactNativeMount.js
@@ -118,7 +118,7 @@ var ReactNativeMount = {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
- ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement);
+ ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement, emptyObject);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
diff --git a/src/renderers/shared/stack/reconciler/ReactUpdateQueue.js b/src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
index 94369f43348da..9d4071cc3ffe9 100644
--- a/src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
+++ b/src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
@@ -246,8 +246,10 @@ var ReactUpdateQueue = {
enqueueUpdate(internalInstance);
},
- enqueueElementInternal: function(internalInstance, newElement) {
- internalInstance._pendingElement = newElement;
+ enqueueElementInternal: function(internalInstance, nextElement, nextContext) {
+ internalInstance._pendingElement = nextElement;
+ // TODO: introduce _pendingContext instead of setting it directly.
+ internalInstance._context = nextContext;
enqueueUpdate(internalInstance);
},