Merge pull request #3640 from jsfb/render-subtree-pass-context

Provide top level method for rendering subtree (passes context)
This commit is contained in:
Jim
2015-04-22 14:29:06 -07:00
6 changed files with 170 additions and 15 deletions
@@ -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 <div>{this.context.foo}</div>;
}
});
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, <Component />, portal);
}.bind(this)).not.toThrow();
}
});
ReactTestUtils.renderIntoDocument(<Parent />);
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 <div>{this.context.foo}</div>;
}
});
var Parent = React.createClass({
childContextTypes: {
foo: React.PropTypes.string.isRequired,
},
getChildContext: function() {
return {
foo: 'bar'
};
},
render: function() {
return null;
},
componentDidMount: function() {
expect(function() {
renderSubtreeIntoContainer(<Parent />, <Component />, portal);
}).toThrow('Invariant Violation: parentComponent' +
'must be a valid React Component');
}
});
});
});
+16
View File
@@ -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;
+2
View File
@@ -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
};
+51 -10
View File
@@ -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`.
+3 -3
View File
@@ -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);
}
}
};
+2 -2
View File
@@ -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);
}
/**