@samitouri / QOS-React-2 / commits / c93b4245cb

Provide top level method for rendering subtree (passes context)

Jim committed Apr 9, 2015 at 13:01 UTC c93b4245cb51484193c5c21d0e8ca727b6025a47
6 files changed +170 -15
src/addons/__tests__/renderSubtreeIntoContainer.js new
+96
@@ -0,0 +1,96 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +'use strict';
13 +
14 +var React = require('React');
15 +var ReactTestUtils = require('ReactTestUtils');
16 +var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
17 +
18 +describe('renderSubtreeIntoContainer', function() {
19 +
20 + it('should pass context when rendering subtree elsewhere', function () {
21 +
22 + var portal = document.createElement('div');
23 +
24 + var Component = React.createClass({
25 + contextTypes: {
26 + foo: React.PropTypes.string.isRequired
27 + },
28 +
29 + render: function() {
30 + return <div>{this.context.foo}</div>;
31 + }
32 + });
33 +
34 + var Parent = React.createClass({
35 + childContextTypes: {
36 + foo: React.PropTypes.string.isRequired,
37 + },
38 +
39 + getChildContext: function() {
40 + return {
41 + foo: 'bar'
42 + };
43 + },
44 +
45 + render: function() {
46 + return null;
47 + },
48 +
49 + componentDidMount: function() {
50 + expect(function() {
51 + renderSubtreeIntoContainer(this, <Component />, portal);
52 + }.bind(this)).not.toThrow();
53 + }
54 + });
55 +
56 + ReactTestUtils.renderIntoDocument(<Parent />);
57 + expect(portal.firstChild.innerHTML).toBe('bar');
58 + });
59 +
60 + it('should throw if parentComponent is invalid', function () {
61 + var portal = document.createElement('div');
62 +
63 + var Component = React.createClass({
64 + contextTypes: {
65 + foo: React.PropTypes.string.isRequired
66 + },
67 +
68 + render: function() {
69 + return <div>{this.context.foo}</div>;
70 + }
71 + });
72 +
73 + var Parent = React.createClass({
74 + childContextTypes: {
75 + foo: React.PropTypes.string.isRequired,
76 + },
77 +
78 + getChildContext: function() {
79 + return {
80 + foo: 'bar'
81 + };
82 + },
83 +
84 + render: function() {
85 + return null;
86 + },
87 +
88 + componentDidMount: function() {
89 + expect(function() {
90 + renderSubtreeIntoContainer(<Parent />, <Component />, portal);
91 + }).toThrow('Invariant Violation: parentComponent' +
92 + 'must be a valid React Component');
93 + }
94 + });
95 + });
96 +});
src/addons/renderSubtreeIntoContainer.js new
+16
@@ -0,0 +1,16 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 +* @providesModule renderSubtreeIntoContainer
10 +*/
11 +
12 +'use strict';
13 +
14 +var ReactMount = require('ReactMount');
15 +
16 +module.exports = ReactMount.renderSubtreeIntoContainer;
src/browser/ReactWithAddons.js
+2
@@ -28,6 +28,7 @@ var ReactTransitionGroup = require('ReactTransitionGroup');
28 var ReactUpdates = require('ReactUpdates');
29
30 var cloneWithProps = require('cloneWithProps');
31 +var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
32 var shallowCompare = require('shallowCompare');
33 var update = require('update');
34
@@ -40,6 +41,7 @@ React.addons = {
41 batchedUpdates: ReactUpdates.batchedUpdates,
42 cloneWithProps: cloneWithProps,
43 createFragment: ReactFragment.create,
44 + renderSubtreeIntoContainer: renderSubtreeIntoContainer,
45 shallowCompare: shallowCompare,
46 update: update
47 };
src/browser/ui/ReactMount.js
+51 -10
@@ -262,10 +262,12 @@ function mountComponentIntoNode(
262 rootID,
263 container,
264 transaction,
265 - shouldReuseMarkup) {
266 - var context = emptyObject;
265 + shouldReuseMarkup,
266 + context) {
267 if (__DEV__) {
268 - context = {};
268 + if (context === emptyObject) {
269 + context = {};
270 + }
271 context[validateDOMNesting.tagStackContextKey] =
272 [container.nodeName.toLowerCase()];
273 }
@@ -288,7 +290,8 @@ function batchedMountComponentIntoNode(
290 componentInstance,
291 rootID,
292 container,
291 - shouldReuseMarkup) {
293 + shouldReuseMarkup,
294 + context) {
295 var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
296 transaction.perform(
297 mountComponentIntoNode,
@@ -297,7 +300,8 @@ function batchedMountComponentIntoNode(
300 rootID,
301 container,
302 transaction,
300 - shouldReuseMarkup
303 + shouldReuseMarkup,
304 + context
305 );
306 ReactUpdates.ReactReconcileTransaction.release(transaction);
307 }
@@ -402,7 +406,8 @@ var ReactMount = {
406 _renderNewRootComponent: function(
407 nextElement,
408 container,
405 - shouldReuseMarkup
409 + shouldReuseMarkup,
410 + context
411 ) {
412 // Various parts of our code (such as ReactCompositeComponent's
413 // _renderValidatedComponent) assume that calls to render aren't nested;
@@ -432,7 +437,8 @@ var ReactMount = {
437 componentInstance,
438 reactRootID,
439 container,
435 - shouldReuseMarkup
440 + shouldReuseMarkup,
441 + context
442 );
443
444 if (__DEV__) {
@@ -451,12 +457,26 @@ var ReactMount = {
457 * perform an update on it and only mutate the DOM as necessary to reflect the
458 * latest React component.
459 *
460 + * @param {ReactComponent} parentComponent The conceptual parent of this render tree.
461 * @param {ReactElement} nextElement Component element to render.
462 * @param {DOMElement} container DOM element to render into.
463 * @param {?function} callback function triggered on completion
464 * @return {ReactComponent} Component instance rendered in `container`.
465 */
459 - render: function(nextElement, container, callback) {
466 + renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
467 + invariant(
468 + parentComponent != null && parentComponent._reactInternalInstance != null,
469 + 'parentComponent must be a valid React Component'
470 + );
471 + return ReactMount._renderSubtreeIntoContainer(
472 + parentComponent,
473 + nextElement,
474 + container,
475 + callback
476 + );
477 + },
478 +
479 + _renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
480 invariant(
481 ReactElement.isValidElement(nextElement),
482 'React.render(): Invalid component element.%s',
@@ -524,11 +544,15 @@ var ReactMount = {
544 }
545
546 var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;
527 -
547 var component = ReactMount._renderNewRootComponent(
548 nextElement,
549 container,
531 - shouldReuseMarkup
550 + shouldReuseMarkup,
551 + parentComponent != null ?
552 + parentComponent._reactInternalInstance._processChildContext(
553 + parentComponent._reactInternalInstance._context
554 + ) :
555 + emptyObject
556 ).getPublicInstance();
557 if (callback) {
558 callback.call(component);
@@ -536,6 +560,23 @@ var ReactMount = {
560 return component;
561 },
562
563 +
564 + /**
565 + * Renders a React component into the DOM in the supplied `container`.
566 + *
567 + * If the React component was previously rendered into `container`, this will
568 + * perform an update on it and only mutate the DOM as necessary to reflect the
569 + * latest React component.
570 + *
571 + * @param {ReactElement} nextElement Component element to render.
572 + * @param {DOMElement} container DOM element to render into.
573 + * @param {?function} callback function triggered on completion
574 + * @return {ReactComponent} Component instance rendered in `container`.
575 + */
576 + render: function(nextElement, container, callback) {
577 + return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
578 + },
579 +
580 /**
581 * Constructs a component instance of `constructor` with `initialProps` and
582 * renders it into the supplied `container`.
src/core/ReactDefaultBatchingStrategy.js
+3 -3
@@ -54,16 +54,16 @@ var ReactDefaultBatchingStrategy = {
54 * Call the provided function in a context within which calls to `setState`
55 * and friends are batched such that components aren't updated unnecessarily.
56 */
57 - batchedUpdates: function(callback, a, b, c, d) {
57 + batchedUpdates: function(callback, a, b, c, d, e) {
58 var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
59
60 ReactDefaultBatchingStrategy.isBatchingUpdates = true;
61
62 // The code is written this way to avoid extra allocations
63 if (alreadyBatchingUpdates) {
64 - callback(a, b, c, d);
64 + callback(a, b, c, d, e);
65 } else {
66 - transaction.perform(callback, null, a, b, c, d);
66 + transaction.perform(callback, null, a, b, c, d, e);
67 }
68 }
69 };
src/core/ReactUpdates.js
+2 -2
@@ -105,9 +105,9 @@ assign(
105
106 PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
107
108 -function batchedUpdates(callback, a, b, c, d) {
108 +function batchedUpdates(callback, a, b, c, d, e) {
109 ensureInjected();
110 - batchingStrategy.batchedUpdates(callback, a, b, c, d);
110 + batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
111 }
112
113 /**