diff --git a/src/addons/__tests__/renderSubtreeIntoContainer.js b/src/addons/__tests__/renderSubtreeIntoContainer.js
new file mode 100644
index 0000000000..80de6145cf
--- /dev/null
+++ b/src/addons/__tests__/renderSubtreeIntoContainer.js
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+var React = require('React');
+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({
+ contextTypes: {
+ foo: React.PropTypes.string.isRequired
+ },
+
+ render: function() {
+ return
{this.context.foo}
;
+ }
+ });
+
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ },
+
+ getChildContext: function() {
+ return {
+ foo: 'bar'
+ };
+ },
+
+ render: function() {
+ return null;
+ },
+
+ componentDidMount: function() {
+ expect(function() {
+ renderSubtreeIntoContainer(this, , portal);
+ }.bind(this)).not.toThrow();
+ }
+ });
+
+ ReactTestUtils.renderIntoDocument();
+ expect(portal.firstChild.innerHTML).toBe('bar');
+ });
+
+ it('should throw if parentComponent is invalid', function () {
+ var portal = document.createElement('div');
+
+ var Component = React.createClass({
+ contextTypes: {
+ foo: React.PropTypes.string.isRequired
+ },
+
+ render: function() {
+ return {this.context.foo}
;
+ }
+ });
+
+ var Parent = React.createClass({
+ childContextTypes: {
+ foo: React.PropTypes.string.isRequired,
+ },
+
+ getChildContext: function() {
+ return {
+ foo: 'bar'
+ };
+ },
+
+ render: function() {
+ return null;
+ },
+
+ componentDidMount: function() {
+ expect(function() {
+ renderSubtreeIntoContainer(, , portal);
+ }).toThrow('Invariant Violation: parentComponent' +
+ 'must be a valid React Component');
+ }
+ });
+ });
+});
diff --git a/src/addons/renderSubtreeIntoContainer.js b/src/addons/renderSubtreeIntoContainer.js
new file mode 100644
index 0000000000..e145f6e4a1
--- /dev/null
+++ b/src/addons/renderSubtreeIntoContainer.js
@@ -0,0 +1,16 @@
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+* @providesModule renderSubtreeIntoContainer
+*/
+
+'use strict';
+
+var ReactMount = require('ReactMount');
+
+module.exports = ReactMount.renderSubtreeIntoContainer;
diff --git a/src/browser/ReactWithAddons.js b/src/browser/ReactWithAddons.js
index 02c3ca7005..bcf5f06cea 100644
--- a/src/browser/ReactWithAddons.js
+++ b/src/browser/ReactWithAddons.js
@@ -28,6 +28,7 @@ var ReactTransitionGroup = require('ReactTransitionGroup');
var ReactUpdates = require('ReactUpdates');
var cloneWithProps = require('cloneWithProps');
+var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
var shallowCompare = require('shallowCompare');
var update = require('update');
@@ -40,6 +41,7 @@ React.addons = {
batchedUpdates: ReactUpdates.batchedUpdates,
cloneWithProps: cloneWithProps,
createFragment: ReactFragment.create,
+ renderSubtreeIntoContainer: renderSubtreeIntoContainer,
shallowCompare: shallowCompare,
update: update
};
diff --git a/src/browser/ui/ReactMount.js b/src/browser/ui/ReactMount.js
index 9e64fd86d0..a7afef275f 100644
--- a/src/browser/ui/ReactMount.js
+++ b/src/browser/ui/ReactMount.js
@@ -262,10 +262,12 @@ function mountComponentIntoNode(
rootID,
container,
transaction,
- shouldReuseMarkup) {
- var context = emptyObject;
+ shouldReuseMarkup,
+ context) {
if (__DEV__) {
- context = {};
+ if (context === emptyObject) {
+ context = {};
+ }
context[validateDOMNesting.tagStackContextKey] =
[container.nodeName.toLowerCase()];
}
@@ -288,7 +290,8 @@ function batchedMountComponentIntoNode(
componentInstance,
rootID,
container,
- shouldReuseMarkup) {
+ shouldReuseMarkup,
+ context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
@@ -297,7 +300,8 @@ function batchedMountComponentIntoNode(
rootID,
container,
transaction,
- shouldReuseMarkup
+ shouldReuseMarkup,
+ context
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
@@ -402,7 +406,8 @@ var ReactMount = {
_renderNewRootComponent: function(
nextElement,
container,
- shouldReuseMarkup
+ shouldReuseMarkup,
+ context
) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
@@ -432,7 +437,8 @@ var ReactMount = {
componentInstance,
reactRootID,
container,
- shouldReuseMarkup
+ shouldReuseMarkup,
+ context
);
if (__DEV__) {
@@ -451,12 +457,26 @@ var ReactMount = {
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
+ * @param {ReactComponent} parentComponent The conceptual parent of this render tree.
* @param {ReactElement} nextElement Component element to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
- render: function(nextElement, container, callback) {
+ renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
+ invariant(
+ parentComponent != null && parentComponent._reactInternalInstance != null,
+ 'parentComponent must be a valid React Component'
+ );
+ return ReactMount._renderSubtreeIntoContainer(
+ parentComponent,
+ nextElement,
+ container,
+ callback
+ );
+ },
+
+ _renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
invariant(
ReactElement.isValidElement(nextElement),
'React.render(): Invalid component element.%s',
@@ -524,11 +544,15 @@ var ReactMount = {
}
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;
-
var component = ReactMount._renderNewRootComponent(
nextElement,
container,
- shouldReuseMarkup
+ shouldReuseMarkup,
+ parentComponent != null ?
+ parentComponent._reactInternalInstance._processChildContext(
+ parentComponent._reactInternalInstance._context
+ ) :
+ emptyObject
).getPublicInstance();
if (callback) {
callback.call(component);
@@ -536,6 +560,23 @@ var ReactMount = {
return component;
},
+
+ /**
+ * Renders a React component into the DOM in the supplied `container`.
+ *
+ * If the React component was previously rendered into `container`, this will
+ * perform an update on it and only mutate the DOM as necessary to reflect the
+ * latest React component.
+ *
+ * @param {ReactElement} nextElement Component element to render.
+ * @param {DOMElement} container DOM element to render into.
+ * @param {?function} callback function triggered on completion
+ * @return {ReactComponent} Component instance rendered in `container`.
+ */
+ render: function(nextElement, container, callback) {
+ return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
+ },
+
/**
* Constructs a component instance of `constructor` with `initialProps` and
* renders it into the supplied `container`.
diff --git a/src/core/ReactDefaultBatchingStrategy.js b/src/core/ReactDefaultBatchingStrategy.js
index 6083799d6e..de856c7762 100644
--- a/src/core/ReactDefaultBatchingStrategy.js
+++ b/src/core/ReactDefaultBatchingStrategy.js
@@ -54,16 +54,16 @@ var ReactDefaultBatchingStrategy = {
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
- batchedUpdates: function(callback, a, b, c, d) {
+ batchedUpdates: function(callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
- callback(a, b, c, d);
+ callback(a, b, c, d, e);
} else {
- transaction.perform(callback, null, a, b, c, d);
+ transaction.perform(callback, null, a, b, c, d, e);
}
}
};
diff --git a/src/core/ReactUpdates.js b/src/core/ReactUpdates.js
index 1c7aea1a13..29ca26641e 100644
--- a/src/core/ReactUpdates.js
+++ b/src/core/ReactUpdates.js
@@ -105,9 +105,9 @@ assign(
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
-function batchedUpdates(callback, a, b, c, d) {
+function batchedUpdates(callback, a, b, c, d, e) {
ensureInjected();
- batchingStrategy.batchedUpdates(callback, a, b, c, d);
+ batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}
/**