From 2b7dce1ca52b3f730d155d27e2552916de6922fc Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Mon, 1 Jun 2015 18:39:05 +0300 Subject: [PATCH 01/17] Pure Functions as Stateless Components --- .../shared/reconciler/ReactNativeComponent.js | 29 ++++++++++++++++++- .../__tests__/ReactCompositeComponent-test.js | 17 +++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactNativeComponent.js b/src/renderers/shared/reconciler/ReactNativeComponent.js index cc7e1ed5a6783..c5494946a6f07 100644 --- a/src/renderers/shared/reconciler/ReactNativeComponent.js +++ b/src/renderers/shared/reconciler/ReactNativeComponent.js @@ -43,6 +43,29 @@ var ReactNativeComponentInjection = { }, }; +/** + * Wrap stateless function by component class. + + * @internal + * @param {function} Stateless component function. + * @return {function} The React class constructor function. + */ +function wrapStatelessFunction(fn) { + if (!fn.ReactComponent) { + fn.ReactComponent = function(props) { + return { + render: function() { + return fn(props); + } + }; + }; + + fn.ReactComponent.displayName = fn.name; + } + + return fn.ReactComponent; +} + /** * Get a composite component wrapper class for a specific tag. * @@ -51,7 +74,11 @@ var ReactNativeComponentInjection = { */ function getComponentClassForElement(element) { if (typeof element.type === 'function') { - return element.type; + if (element.type.prototype.render) { + return element.type; + } else { + return wrapStatelessFunction(element.type); + } } var tag = element.type; var componentClass = tagToComponentClass[tag]; diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index c23ea8c98f899..16a36c17297a1 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -845,17 +845,14 @@ describe('ReactCompositeComponent', function() { expect(a).toBe(b); }); - it('should warn when using non-React functions in JSX', function() { - function NotAComponent() { - return [
,
]; + it('should render stateless functions', function() { + function StatelessComponent(props) { + return
{props.name}
; } - expect(function() { - ReactTestUtils.renderIntoDocument(
); - }).toThrow(); // has no method 'render' - expect(console.error.calls.length).toBe(1); - expect(console.error.calls[0].args[0]).toContain( - 'NotAComponent(...): No `render` method found' - ); + + var comp = ReactTestUtils.renderIntoDocument(); + + expect(React.findDOMNode(comp).textContent).toBe('A'); }); it('context should be passed down from the parent', function() { From 232fab6b64890fea1b719a9f29cf074f6510c2c1 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Tue, 2 Jun 2015 01:16:51 +0300 Subject: [PATCH 02/17] WIP --- .../shared/reconciler/ReactNativeComponent.js | 29 +- .../reconciler/ReactStatelessComponent.js | 390 ++++++++++++++++++ .../__tests__/ReactCompositeComponent-test.js | 10 - .../__tests__/ReactStatelessComponent-test.js | 61 +++ .../reconciler/instantiateReactComponent.js | 25 ++ 5 files changed, 477 insertions(+), 38 deletions(-) create mode 100644 src/renderers/shared/reconciler/ReactStatelessComponent.js create mode 100644 src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js diff --git a/src/renderers/shared/reconciler/ReactNativeComponent.js b/src/renderers/shared/reconciler/ReactNativeComponent.js index c5494946a6f07..cc7e1ed5a6783 100644 --- a/src/renderers/shared/reconciler/ReactNativeComponent.js +++ b/src/renderers/shared/reconciler/ReactNativeComponent.js @@ -43,29 +43,6 @@ var ReactNativeComponentInjection = { }, }; -/** - * Wrap stateless function by component class. - - * @internal - * @param {function} Stateless component function. - * @return {function} The React class constructor function. - */ -function wrapStatelessFunction(fn) { - if (!fn.ReactComponent) { - fn.ReactComponent = function(props) { - return { - render: function() { - return fn(props); - } - }; - }; - - fn.ReactComponent.displayName = fn.name; - } - - return fn.ReactComponent; -} - /** * Get a composite component wrapper class for a specific tag. * @@ -74,11 +51,7 @@ function wrapStatelessFunction(fn) { */ function getComponentClassForElement(element) { if (typeof element.type === 'function') { - if (element.type.prototype.render) { - return element.type; - } else { - return wrapStatelessFunction(element.type); - } + return element.type; } var tag = element.type; var componentClass = tagToComponentClass[tag]; diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js new file mode 100644 index 0000000000000..2f428da19a6f6 --- /dev/null +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -0,0 +1,390 @@ +/** + * 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 ReactStatelessComponent + */ + +'use strict'; + +var ReactComponentEnvironment = require('ReactComponentEnvironment'); +var ReactContext = require('ReactContext'); +var ReactCurrentOwner = require('ReactCurrentOwner'); +var ReactElement = require('ReactElement'); +var ReactInstanceMap = require('ReactInstanceMap'); +var ReactLifeCycle = require('ReactLifeCycle'); +var ReactPerf = require('ReactPerf'); +var ReactReconciler = require('ReactReconciler'); + +var emptyObject = require('emptyObject'); +var invariant = require('invariant'); +var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); + +/** + * An incrementing ID assigned to each component when it is mounted. This is + * used to enforce the order in which `ReactUpdates` updates dirty components. + * + * @private + */ +var nextMountID = 1; + +/** + * @lends {ReactStatelessComponent.prototype} + */ +var ReactStatelessComponentMixin = { + + /** + * Base constructor for all stateless component. + * + * @param {ReactElement} element + * @final + * @internal + */ + construct: function(element) { + this._currentElement = element; + this._rootNodeID = null; + this._instance = null; + + this._renderedComponent = null; + + this._context = null; + this._mountOrder = 0; + this._isTopLevel = false; + }, + + /** + * Initializes the component, renders markup, and registers event listeners. + * + * @param {string} rootID DOM ID of the root node. + * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction + * @return {?string} Rendered markup to be inserted into the DOM. + * @final + * @internal + */ + mountComponent: function(rootID, transaction, context) { + this._context = context; + this._mountOrder = nextMountID++; + this._rootNodeID = rootID; + + var publicProps = this._currentElement.props; + + // Initialize the public class + var inst = {}; + + // These should be set up in the constructor, but as a convenience for + // simpler class abstractions, we set them up after the fact. + inst.props = publicProps; + inst.refs = emptyObject; + + this._instance = inst; + + // Store a reference from the instance back to the internal representation + ReactInstanceMap.set(inst, this); + + var renderedElement; + + var previouslyMounting = ReactLifeCycle.currentlyMountingInstance; + ReactLifeCycle.currentlyMountingInstance = this; + try { + renderedElement = this._renderValidatedComponent(); + } finally { + ReactLifeCycle.currentlyMountingInstance = previouslyMounting; + } + + this._renderedComponent = this._instantiateReactComponent( + renderedElement, + this._currentElement.type // The wrapping type + ); + + var markup = ReactReconciler.mountComponent( + this._renderedComponent, + rootID, + transaction, + context + ); + + return markup; + }, + + /** + * Releases any resources allocated by `mountComponent`. + * + * @final + * @internal + */ + unmountComponent: function() { + var inst = this._instance; + + ReactReconciler.unmountComponent(this._renderedComponent); + this._renderedComponent = null; + + // These fields do not really need to be reset since this object is no + // longer accessible. + this._context = null; + this._rootNodeID = null; + + // Delete the reference from the instance to this internal representation + // which allow the internals to be properly cleaned up even if the user + // leaks a reference to the public instance. + ReactInstanceMap.remove(inst); + }, + + receiveComponent: function(nextElement, transaction, nextContext) { + var prevElement = this._currentElement; + + this.updateComponent( + transaction, + prevElement, + nextElement + ); + }, + + /** + * @param {ReactReconcileTransaction} transaction + * @internal + */ + performUpdateIfNecessary: function(transaction) { + }, + + /** + * Perform an update to a mounted component. The componentWillReceiveProps and + * shouldComponentUpdate methods are called, then (assuming the update isn't + * skipped) the remaining update lifecycle methods are called and the DOM + * representation is updated. + * + * By default, this implements React's rendering and reconciliation algorithm. + * Sophisticated clients may wish to override this. + * + * @param {ReactReconcileTransaction} transaction + * @param {ReactElement} prevParentElement + * @param {ReactElement} nextParentElement + * @internal + * @overridable + */ + updateComponent: function( + transaction, + prevParentElement, + nextParentElement, + prevUnmaskedContext, + nextUnmaskedContext + ) { + var inst = this._instance; + + var nextProps = inst.props; + + // Distinguish between a props update versus a simple state update + if (prevParentElement !== nextParentElement) { + nextProps = nextParentElement.props; + } + + var shouldUpdate = true; + + if (shouldUpdate) { + // Will set `this.props` + this._performComponentUpdate( + nextParentElement, + nextProps, + transaction + ); + } else { + // If it's determined that a component should not update, we still want + // to set props and state but we shortcut the rest of the update. + this._currentElement = nextParentElement; + this._context = nextUnmaskedContext; + inst.props = nextProps; + } + }, + + /** + * Merges new props and state, notifies delegate methods of update and + * performs update. + * + * @param {ReactElement} nextElement Next element + * @param {object} nextProps Next public object to set as properties. + * @param {ReactReconcileTransaction} transaction + * @private + */ + _performComponentUpdate: function( + nextElement, + nextProps, + transaction + ) { + var inst = this._instance; + + this._currentElement = nextElement; + inst.props = nextProps; + + this._updateRenderedComponent(transaction); + }, + + /** + * Call the component's `render` method and update the DOM accordingly. + * + * @param {ReactReconcileTransaction} transaction + * @internal + */ + _updateRenderedComponent: function(transaction) { + var prevComponentInstance = this._renderedComponent; + var prevRenderedElement = prevComponentInstance._currentElement; + var nextRenderedElement = this._renderValidatedComponent(); + if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { + ReactReconciler.receiveComponent( + prevComponentInstance, + nextRenderedElement, + transaction, + this._context + ); + } else { + // These two IDs are actually the same! But nothing should rely on that. + var thisID = this._rootNodeID; + var prevComponentID = prevComponentInstance._rootNodeID; + ReactReconciler.unmountComponent(prevComponentInstance); + + this._renderedComponent = this._instantiateReactComponent( + nextRenderedElement, + this._currentElement.type + ); + var nextMarkup = ReactReconciler.mountComponent( + this._renderedComponent, + thisID, + transaction, + this._context + ); + this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup); + } + }, + + /** + * @protected + */ + _replaceNodeWithMarkupByID: function(prevComponentID, nextMarkup) { + ReactComponentEnvironment.replaceNodeWithMarkupByID( + prevComponentID, + nextMarkup + ); + }, + + /** + * @protected + */ + _renderValidatedComponentWithoutOwnerOrContext: function() { + var inst = this._instance; + var render = this._currentElement.type; + var renderedComponent = render(inst.props); + if (__DEV__) { + // We allow auto-mocks to proceed as if they're returning null. + if (typeof renderedComponent === 'undefined' && + render._isMockFunction) { + // This is probably bad practice. Consider warning here and + // deprecating this convenience. + renderedComponent = null; + } + } + + return renderedComponent; + }, + + /** + * @private + */ + _renderValidatedComponent: function() { + var renderedComponent; + var previousContext = ReactContext.current; + ReactContext.current = this._currentElement._context; + ReactCurrentOwner.current = this; + try { + renderedComponent = + this._renderValidatedComponentWithoutOwnerOrContext(); + } finally { + ReactContext.current = previousContext; + ReactCurrentOwner.current = null; + } + invariant( + // TODO: An `isValidNode` function would probably be more appropriate + renderedComponent === null || renderedComponent === false || + ReactElement.isValidElement(renderedComponent), + '%s.render(): A valid ReactComponent must be returned. You may have ' + + 'returned undefined, an array or some other invalid object.', + this.getName() || 'ReactStatelessComponent' + ); + return renderedComponent; + }, + + /** + * Lazily allocates the refs object and stores `component` as `ref`. + * + * @param {string} ref Reference name. + * @param {component} component Component to store as `ref`. + * @final + * @private + */ + attachRef: function(ref, component) { + var inst = this.getPublicInstance(); + var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs; + refs[ref] = component.getPublicInstance(); + }, + + /** + * Detaches a reference name. + * + * @param {string} ref Name to dereference. + * @final + * @private + */ + detachRef: function(ref) { + var refs = this.getPublicInstance().refs; + delete refs[ref]; + }, + + /** + * Get a text description of the component that can be used to identify it + * in error messages. + * @return {string} The name or null. + * @internal + */ + getName: function() { + var type = this._currentElement.type; + + return ( + type.displayName || type.name || null + ); + }, + + /** + * Get the publicly accessible representation of this component - i.e. what + * is exposed by refs and returned by React.render. Can be null for stateless + * components. + * + * @return {ReactComponent} the public component instance. + * @internal + */ + getPublicInstance: function() { + return this._instance; + }, + + // Stub + _instantiateReactComponent: null + +}; + +ReactPerf.measureMethods( + ReactStatelessComponentMixin, + 'ReactStatelessComponent', + { + mountComponent: 'mountComponent', + updateComponent: 'updateComponent', + _renderValidatedComponent: '_renderValidatedComponent' + } +); + +var ReactStatelessComponent = { + + Mixin: ReactStatelessComponentMixin + +}; + +module.exports = ReactStatelessComponent; diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index 16a36c17297a1..d7b412884aa08 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -845,16 +845,6 @@ describe('ReactCompositeComponent', function() { expect(a).toBe(b); }); - it('should render stateless functions', function() { - function StatelessComponent(props) { - return
{props.name}
; - } - - var comp = ReactTestUtils.renderIntoDocument(); - - expect(React.findDOMNode(comp).textContent).toBe('A'); - }); - it('context should be passed down from the parent', function() { var Parent = React.createClass({ childContextTypes: { diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js new file mode 100644 index 0000000000000..b176ff8b066fa --- /dev/null +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -0,0 +1,61 @@ +/** + * 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; +var ReactTestUtils; +var StatelessComponent; + +describe('ReactStatelessComponent', function() { + + beforeEach(function() { + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + + StatelessComponent = function StatelessComponent(props) { + return
{props.name}
; + } + }); + + it('should render stateless component', function() { + var comp = ReactTestUtils.renderIntoDocument( + + ); + + expect(React.findDOMNode(comp).textContent).toBe('A'); + }); + + it('should update stateless component', function() { + var Parent = React.createClass({ + render() { + return ; + } + }); + + var comp = ReactTestUtils.renderIntoDocument(); + expect(React.findDOMNode(comp).textContent).toBe('A'); + + comp.setProps({name: 'B'}); + expect(React.findDOMNode(comp).textContent).toBe('B'); + }); + + it('should unmount stateless component', function() { + var container = document.createElement('div'); + + React.render(, container); + expect(container.textContent).toBe('A'); + + React.unmountComponentAtNode(container); + expect(container.textContent).toBe(''); + }); + +}); diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js index 9f470c04c1ae8..7426b4ceff800 100644 --- a/src/renderers/shared/reconciler/instantiateReactComponent.js +++ b/src/renderers/shared/reconciler/instantiateReactComponent.js @@ -15,6 +15,7 @@ var ReactCompositeComponent = require('ReactCompositeComponent'); var ReactEmptyComponent = require('ReactEmptyComponent'); var ReactNativeComponent = require('ReactNativeComponent'); +var ReactStatelessComponent = require('ReactStatelessComponent'); var assign = require('Object.assign'); var invariant = require('invariant'); @@ -29,6 +30,14 @@ assign( _instantiateReactComponent: instantiateReactComponent, } ); +var ReactStatelessComponentWrapper = function() { }; +assign( + ReactStatelessComponentWrapper.prototype, + ReactStatelessComponent.Mixin, + { + _instantiateReactComponent: instantiateReactComponent + } +); /** * Check if the type reference is a known internal type. I.e. not a user @@ -46,6 +55,20 @@ function isInternalComponentType(type) { ); } +/** + * Check if the type reference is a stateless function type. + * + * @param {function} type + * @return {boolean} Returns true if this is a stateless function type. + */ +function isStatelessComponentType(type) { + return ( + typeof type === 'function' && + (typeof type.prototype === 'undefined' || + typeof type.prototype.render !== 'function') + ); +} + /** * Given a ReactNode, create an instance that will actually be mounted. * @@ -83,6 +106,8 @@ function instantiateReactComponent(node, parentCompositeType) { // represenations. I.e. ART. Once those are updated to use the string // representation, we can drop this code path. instance = new element.type(element); + } else if (isStatelessComponentType(element.type)) { + instance = new ReactStatelessComponentWrapper(); } else { instance = new ReactCompositeComponentWrapper(); } From 0df2b5d406e550681d4cfd5964dddaa7fc389e89 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Tue, 2 Jun 2015 16:25:21 +0300 Subject: [PATCH 03/17] WIP: added failed test --- .../__tests__/ReactStatelessComponent-test.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index b176ff8b066fa..5004bd5891c33 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -58,4 +58,49 @@ describe('ReactStatelessComponent', function() { expect(container.textContent).toBe(''); }); + it('should pass context thru stateless component', function() { + var Child = React.createClass({ + contextTypes: { + test: React.PropTypes.string.isRequired + }, + + render: function() { + return
{this.context.test}
; + } + }) + + function Parent() { + return ; + } + // var Parent = React.createClass({ + // render: function() { + // return ; + // } + // }); + + var GrandParent = React.createClass({ + childContextTypes: { + test: React.PropTypes.string.isRequired + }, + + getChildContext() { + return {test: this.props.test}; + }, + + render: function() { + return ; + } + }) + + var comp = ReactTestUtils.renderIntoDocument( + + ); + + expect(React.findDOMNode(comp).textContent).toBe('test'); + + comp.setProps({test: 'mest'}); + + expect(React.findDOMNode(comp).textContent).toBe('mest'); + }); + }); From 987082dcb431b8d5c190326934696d10c2fcd01f Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Tue, 2 Jun 2015 16:33:29 +0300 Subject: [PATCH 04/17] WIP: Test fix --- .../shared/reconciler/ReactStatelessComponent.js | 14 ++++++++++---- .../__tests__/ReactStatelessComponent-test.js | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 2f428da19a6f6..3736fb0aaf8cc 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -135,11 +135,14 @@ var ReactStatelessComponentMixin = { receiveComponent: function(nextElement, transaction, nextContext) { var prevElement = this._currentElement; + var prevContext = this._context; this.updateComponent( transaction, prevElement, - nextElement + nextElement, + prevContext, + nextContext ); }, @@ -169,8 +172,8 @@ var ReactStatelessComponentMixin = { transaction, prevParentElement, nextParentElement, - prevUnmaskedContext, - nextUnmaskedContext + prevContext, + nextContext ) { var inst = this._instance; @@ -188,13 +191,14 @@ var ReactStatelessComponentMixin = { this._performComponentUpdate( nextParentElement, nextProps, + nextContext, transaction ); } else { // If it's determined that a component should not update, we still want // to set props and state but we shortcut the rest of the update. this._currentElement = nextParentElement; - this._context = nextUnmaskedContext; + this._context = nextContext; inst.props = nextProps; } }, @@ -211,11 +215,13 @@ var ReactStatelessComponentMixin = { _performComponentUpdate: function( nextElement, nextProps, + nextContext, transaction ) { var inst = this._instance; this._currentElement = nextElement; + this._context = nextContext; inst.props = nextProps; this._updateRenderedComponent(transaction); diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index 5004bd5891c33..0faeed60c5e36 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -67,7 +67,7 @@ describe('ReactStatelessComponent', function() { render: function() { return
{this.context.test}
; } - }) + }); function Parent() { return ; @@ -90,7 +90,7 @@ describe('ReactStatelessComponent', function() { render: function() { return ; } - }) + }); var comp = ReactTestUtils.renderIntoDocument( From 6e47c02d168722be2050af828f5fc5aeff8ef1ae Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Tue, 2 Jun 2015 16:42:02 +0300 Subject: [PATCH 05/17] WIP: Lint fix --- .../shared/reconciler/__tests__/ReactStatelessComponent-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index 0faeed60c5e36..615fefb06dbcd 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -23,7 +23,7 @@ describe('ReactStatelessComponent', function() { StatelessComponent = function StatelessComponent(props) { return
{props.name}
; - } + }; }); it('should render stateless component', function() { From da77a5c28c1088e55aa1b0aca65de2f2c3f881b3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:17:39 +0300 Subject: [PATCH 06/17] WIP: Remove attachRef and detachRef --- .../reconciler/ReactStatelessComponent.js | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 3736fb0aaf8cc..9e8530de5be9b 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -320,32 +320,6 @@ var ReactStatelessComponentMixin = { return renderedComponent; }, - /** - * Lazily allocates the refs object and stores `component` as `ref`. - * - * @param {string} ref Reference name. - * @param {component} component Component to store as `ref`. - * @final - * @private - */ - attachRef: function(ref, component) { - var inst = this.getPublicInstance(); - var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs; - refs[ref] = component.getPublicInstance(); - }, - - /** - * Detaches a reference name. - * - * @param {string} ref Name to dereference. - * @final - * @private - */ - detachRef: function(ref) { - var refs = this.getPublicInstance().refs; - delete refs[ref]; - }, - /** * Get a text description of the component that can be used to identify it * in error messages. From 7cdcb39b41567d2a410a608ddd56888edaff8a65 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:20:41 +0300 Subject: [PATCH 07/17] WIP: Always should update --- .../reconciler/ReactStatelessComponent.js | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 9e8530de5be9b..68753a789e496 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -184,23 +184,12 @@ var ReactStatelessComponentMixin = { nextProps = nextParentElement.props; } - var shouldUpdate = true; - - if (shouldUpdate) { - // Will set `this.props` - this._performComponentUpdate( - nextParentElement, - nextProps, - nextContext, - transaction - ); - } else { - // If it's determined that a component should not update, we still want - // to set props and state but we shortcut the rest of the update. - this._currentElement = nextParentElement; - this._context = nextContext; - inst.props = nextProps; - } + this._performComponentUpdate( + nextParentElement, + nextProps, + nextContext, + transaction + ); }, /** From f46268f83bbe5da268b9a06e10bcd54f26a5dcf0 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:22:12 +0300 Subject: [PATCH 08/17] WIP: Remove nextMountId and mountOrder --- .../shared/reconciler/ReactStatelessComponent.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 68753a789e496..c5e4f272bac11 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -24,14 +24,6 @@ var emptyObject = require('emptyObject'); var invariant = require('invariant'); var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); -/** - * An incrementing ID assigned to each component when it is mounted. This is - * used to enforce the order in which `ReactUpdates` updates dirty components. - * - * @private - */ -var nextMountID = 1; - /** * @lends {ReactStatelessComponent.prototype} */ @@ -52,7 +44,6 @@ var ReactStatelessComponentMixin = { this._renderedComponent = null; this._context = null; - this._mountOrder = 0; this._isTopLevel = false; }, @@ -67,7 +58,6 @@ var ReactStatelessComponentMixin = { */ mountComponent: function(rootID, transaction, context) { this._context = context; - this._mountOrder = nextMountID++; this._rootNodeID = rootID; var publicProps = this._currentElement.props; From 5ff4f76cf91eb6bdae3f23e1f638a90ae7c0bf25 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:35:30 +0300 Subject: [PATCH 09/17] WIP: Merge _renderValidatedComponentWithoutOwnerOrContext into _renderValidatedComponent --- .../reconciler/ReactStatelessComponent.js | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index c5e4f272bac11..76d89f85bb4b8 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -253,37 +253,27 @@ var ReactStatelessComponentMixin = { ); }, - /** - * @protected - */ - _renderValidatedComponentWithoutOwnerOrContext: function() { - var inst = this._instance; - var render = this._currentElement.type; - var renderedComponent = render(inst.props); - if (__DEV__) { - // We allow auto-mocks to proceed as if they're returning null. - if (typeof renderedComponent === 'undefined' && - render._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - renderedComponent = null; - } - } - - return renderedComponent; - }, - /** * @private */ _renderValidatedComponent: function() { var renderedComponent; + var inst = this._instance; + var render = this._currentElement.type; var previousContext = ReactContext.current; ReactContext.current = this._currentElement._context; ReactCurrentOwner.current = this; try { - renderedComponent = - this._renderValidatedComponentWithoutOwnerOrContext(); + renderedComponent = render(inst.props); + if (__DEV__) { + // We allow auto-mocks to proceed as if they're returning null. + if (typeof renderedComponent === 'undefined' && + render._isMockFunction) { + // This is probably bad practice. Consider warning here and + // deprecating this convenience. + renderedComponent = null; + } + } } finally { ReactContext.current = previousContext; ReactCurrentOwner.current = null; From ab3c531bd351f800ffd70877b163d28802f4c8b9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:36:26 +0300 Subject: [PATCH 10/17] WIP: Merge _replaceNodeWithMarkupByID into _updateRenderedComponent --- .../shared/reconciler/ReactStatelessComponent.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 76d89f85bb4b8..6fec3ed64b22b 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -239,20 +239,13 @@ var ReactStatelessComponentMixin = { transaction, this._context ); - this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup); + ReactComponentEnvironment.replaceNodeWithMarkupByID( + prevComponentID, + nextMarkup + ); } }, - /** - * @protected - */ - _replaceNodeWithMarkupByID: function(prevComponentID, nextMarkup) { - ReactComponentEnvironment.replaceNodeWithMarkupByID( - prevComponentID, - nextMarkup - ); - }, - /** * @private */ From a2088ea04ce13772949dc939bbf7f71c47843e33 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:37:57 +0300 Subject: [PATCH 11/17] WIP: Merge _performComponentUpdate into updateComponent --- .../reconciler/ReactStatelessComponent.js | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index 6fec3ed64b22b..d93cf6afa8cea 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -167,41 +167,13 @@ var ReactStatelessComponentMixin = { ) { var inst = this._instance; - var nextProps = inst.props; - // Distinguish between a props update versus a simple state update if (prevParentElement !== nextParentElement) { - nextProps = nextParentElement.props; + inst.props = nextParentElement.props; } - this._performComponentUpdate( - nextParentElement, - nextProps, - nextContext, - transaction - ); - }, - - /** - * Merges new props and state, notifies delegate methods of update and - * performs update. - * - * @param {ReactElement} nextElement Next element - * @param {object} nextProps Next public object to set as properties. - * @param {ReactReconcileTransaction} transaction - * @private - */ - _performComponentUpdate: function( - nextElement, - nextProps, - nextContext, - transaction - ) { - var inst = this._instance; - - this._currentElement = nextElement; + this._currentElement = nextParentElement; this._context = nextContext; - inst.props = nextProps; this._updateRenderedComponent(transaction); }, From 463155e3303cc9eb172096ecfafc423181869458 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:41:58 +0300 Subject: [PATCH 12/17] WIP: Merge _updateRenderedComponent into updateComponent and remove this._context --- .../reconciler/ReactStatelessComponent.js | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index d93cf6afa8cea..cdeee649fd8ef 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -43,7 +43,6 @@ var ReactStatelessComponentMixin = { this._renderedComponent = null; - this._context = null; this._isTopLevel = false; }, @@ -57,7 +56,6 @@ var ReactStatelessComponentMixin = { * @internal */ mountComponent: function(rootID, transaction, context) { - this._context = context; this._rootNodeID = rootID; var publicProps = this._currentElement.props; @@ -114,7 +112,6 @@ var ReactStatelessComponentMixin = { // These fields do not really need to be reset since this object is no // longer accessible. - this._context = null; this._rootNodeID = null; // Delete the reference from the instance to this internal representation @@ -125,13 +122,11 @@ var ReactStatelessComponentMixin = { receiveComponent: function(nextElement, transaction, nextContext) { var prevElement = this._currentElement; - var prevContext = this._context; this.updateComponent( transaction, prevElement, nextElement, - prevContext, nextContext ); }, @@ -162,7 +157,6 @@ var ReactStatelessComponentMixin = { transaction, prevParentElement, nextParentElement, - prevContext, nextContext ) { var inst = this._instance; @@ -173,18 +167,7 @@ var ReactStatelessComponentMixin = { } this._currentElement = nextParentElement; - this._context = nextContext; - this._updateRenderedComponent(transaction); - }, - - /** - * Call the component's `render` method and update the DOM accordingly. - * - * @param {ReactReconcileTransaction} transaction - * @internal - */ - _updateRenderedComponent: function(transaction) { var prevComponentInstance = this._renderedComponent; var prevRenderedElement = prevComponentInstance._currentElement; var nextRenderedElement = this._renderValidatedComponent(); @@ -193,7 +176,7 @@ var ReactStatelessComponentMixin = { prevComponentInstance, nextRenderedElement, transaction, - this._context + nextContext ); } else { // These two IDs are actually the same! But nothing should rely on that. @@ -209,7 +192,7 @@ var ReactStatelessComponentMixin = { this._renderedComponent, thisID, transaction, - this._context + nextContext ); ReactComponentEnvironment.replaceNodeWithMarkupByID( prevComponentID, From af9628081ff56d228ac6510444adb8d374dda112 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:45:31 +0300 Subject: [PATCH 13/17] WIP: Remove ReactContext.current and ReactCurrentOwner.current from _renderValidatedComponent --- .../reconciler/ReactStatelessComponent.js | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index cdeee649fd8ef..bdd8aadaed45f 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -205,27 +205,21 @@ var ReactStatelessComponentMixin = { * @private */ _renderValidatedComponent: function() { - var renderedComponent; var inst = this._instance; var render = this._currentElement.type; - var previousContext = ReactContext.current; - ReactContext.current = this._currentElement._context; - ReactCurrentOwner.current = this; - try { - renderedComponent = render(inst.props); - if (__DEV__) { - // We allow auto-mocks to proceed as if they're returning null. - if (typeof renderedComponent === 'undefined' && - render._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - renderedComponent = null; - } + + var renderedComponent = render(inst.props); + + if (__DEV__) { + // We allow auto-mocks to proceed as if they're returning null. + if (typeof renderedComponent === 'undefined' && + render._isMockFunction) { + // This is probably bad practice. Consider warning here and + // deprecating this convenience. + renderedComponent = null; } - } finally { - ReactContext.current = previousContext; - ReactCurrentOwner.current = null; } + invariant( // TODO: An `isValidNode` function would probably be more appropriate renderedComponent === null || renderedComponent === false || @@ -234,6 +228,7 @@ var ReactStatelessComponentMixin = { 'returned undefined, an array or some other invalid object.', this.getName() || 'ReactStatelessComponent' ); + return renderedComponent; }, From 9a2d3b7e2a6e947ca3e27252bb6baf1bf9b8fa1d Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:47:39 +0300 Subject: [PATCH 14/17] WIP: Remove unnecessary requires --- src/renderers/shared/reconciler/ReactStatelessComponent.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index bdd8aadaed45f..f7a7441c5ecd4 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -12,8 +12,6 @@ 'use strict'; var ReactComponentEnvironment = require('ReactComponentEnvironment'); -var ReactContext = require('ReactContext'); -var ReactCurrentOwner = require('ReactCurrentOwner'); var ReactElement = require('ReactElement'); var ReactInstanceMap = require('ReactInstanceMap'); var ReactLifeCycle = require('ReactLifeCycle'); From dc4d1562a66e6f4e14a0b37e60a5c877628cabf1 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 03:51:10 +0300 Subject: [PATCH 15/17] WIP: Make lint happy --- .../shared/reconciler/ReactStatelessComponent.js | 6 +++--- .../__tests__/ReactStatelessComponent-test.js | 10 +++++----- .../shared/reconciler/instantiateReactComponent.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index f7a7441c5ecd4..ce59410fc629b 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -257,7 +257,7 @@ var ReactStatelessComponentMixin = { }, // Stub - _instantiateReactComponent: null + _instantiateReactComponent: null, }; @@ -267,13 +267,13 @@ ReactPerf.measureMethods( { mountComponent: 'mountComponent', updateComponent: 'updateComponent', - _renderValidatedComponent: '_renderValidatedComponent' + _renderValidatedComponent: '_renderValidatedComponent', } ); var ReactStatelessComponent = { - Mixin: ReactStatelessComponentMixin + Mixin: ReactStatelessComponentMixin, }; diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index 615fefb06dbcd..ced3e4059f6d9 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -38,7 +38,7 @@ describe('ReactStatelessComponent', function() { var Parent = React.createClass({ render() { return ; - } + }, }); var comp = ReactTestUtils.renderIntoDocument(); @@ -61,12 +61,12 @@ describe('ReactStatelessComponent', function() { it('should pass context thru stateless component', function() { var Child = React.createClass({ contextTypes: { - test: React.PropTypes.string.isRequired + test: React.PropTypes.string.isRequired, }, render: function() { return
{this.context.test}
; - } + }, }); function Parent() { @@ -80,7 +80,7 @@ describe('ReactStatelessComponent', function() { var GrandParent = React.createClass({ childContextTypes: { - test: React.PropTypes.string.isRequired + test: React.PropTypes.string.isRequired, }, getChildContext() { @@ -89,7 +89,7 @@ describe('ReactStatelessComponent', function() { render: function() { return ; - } + }, }); var comp = ReactTestUtils.renderIntoDocument( diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js index 7426b4ceff800..ba4adab1c0754 100644 --- a/src/renderers/shared/reconciler/instantiateReactComponent.js +++ b/src/renderers/shared/reconciler/instantiateReactComponent.js @@ -35,7 +35,7 @@ assign( ReactStatelessComponentWrapper.prototype, ReactStatelessComponent.Mixin, { - _instantiateReactComponent: instantiateReactComponent + _instantiateReactComponent: instantiateReactComponent, } ); From f821655a1a70df7c36a8c2078bf2c6c46f6109d3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 12 Jun 2015 14:33:30 +0300 Subject: [PATCH 16/17] WIP: Hack: ReactStatelessOrCompositeComponentWrapper --- .../reconciler/ReactCompositeComponent.js | 35 ++++---- .../reconciler/ReactStatelessComponent.js | 18 ----- .../__tests__/ReactStatelessComponent-test.js | 15 ++++ .../reconciler/instantiateReactComponent.js | 80 +++++++++++++++++-- 4 files changed, 108 insertions(+), 40 deletions(-) diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index bd3f4d0dc9f09..1ad23264d37b7 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -119,10 +119,6 @@ var ReactCompositeComponentMixin = { * @internal */ mountComponent: function(rootID, transaction, context) { - this._context = context; - this._mountOrder = nextMountID++; - this._rootNodeID = rootID; - var publicProps = this._processProps(this._currentElement.props); var publicContext = this._processContext(context); @@ -133,19 +129,28 @@ var ReactCompositeComponentMixin = { // Initialize the public class var inst = new Component(publicProps, publicContext); - if (__DEV__) { - // This will throw later in _renderValidatedComponent, but add an early - // warning now to help debugging - warning( - inst.render != null, - '%s(...): No `render` method found on the returned component ' + - 'instance: you may have forgotten to define `render` in your ' + - 'component or you may have accidentally tried to render an element ' + - 'whose type is a function that isn\'t a React component.', - Component.displayName || Component.name || 'Component' - ); + if (!inst.render) { + return false; } + this._context = context; + this._mountOrder = nextMountID++; + this._rootNodeID = rootID; + + // TODO: Move to right place + // if (__DEV__) { + // // This will throw later in _renderValidatedComponent, but add an early + // // warning now to help debugging + // warning( + // inst.render != null, + // '%s(...): No `render` method found on the returned component ' + + // 'instance: you may have forgotten to define `render` in your ' + + // 'component or you may have accidentally tried to render an element ' + + // 'whose type is a function that isn\'t a React component.', + // Component.displayName || Component.name || 'Component' + // ); + // } + // These should be set up in the constructor, but as a convenience for // simpler class abstractions, we set them up after the fact. inst.props = publicProps; diff --git a/src/renderers/shared/reconciler/ReactStatelessComponent.js b/src/renderers/shared/reconciler/ReactStatelessComponent.js index ce59410fc629b..d00ee5fb647aa 100644 --- a/src/renderers/shared/reconciler/ReactStatelessComponent.js +++ b/src/renderers/shared/reconciler/ReactStatelessComponent.js @@ -26,24 +26,6 @@ var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); * @lends {ReactStatelessComponent.prototype} */ var ReactStatelessComponentMixin = { - - /** - * Base constructor for all stateless component. - * - * @param {ReactElement} element - * @final - * @internal - */ - construct: function(element) { - this._currentElement = element; - this._rootNodeID = null; - this._instance = null; - - this._renderedComponent = null; - - this._isTopLevel = false; - }, - /** * Initializes the component, renders markup, and registers event listeners. * diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index ced3e4059f6d9..b5a51d728e751 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -103,4 +103,19 @@ describe('ReactStatelessComponent', function() { expect(React.findDOMNode(comp).textContent).toBe('mest'); }); + it('should support module pattern components', function() { + function Child({test}) { + return { + render() { + return
{test}
+ }, + }; + } + + var comp = ReactTestUtils.renderIntoDocument( + + ); + + expect(React.findDOMNode(comp).textContent).toBe('test'); + }); }); diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js index ba4adab1c0754..d2c06e5d9349c 100644 --- a/src/renderers/shared/reconciler/instantiateReactComponent.js +++ b/src/renderers/shared/reconciler/instantiateReactComponent.js @@ -30,14 +30,80 @@ assign( _instantiateReactComponent: instantiateReactComponent, } ); -var ReactStatelessComponentWrapper = function() { }; +var ReactStatelessOrCompositeComponentWrapper = function() { }; assign( - ReactStatelessComponentWrapper.prototype, - ReactStatelessComponent.Mixin, + ReactStatelessOrCompositeComponentWrapper.prototype, + ReactCompositeComponent.Mixin, { _instantiateReactComponent: instantiateReactComponent, + + construct: function(element) { + ReactCompositeComponent.Mixin.construct.call(this, element); + this._stateless = false; + }, + + mountComponent: function(rootID, transaction, context) { + // Will return false if element isn't composite + var result = ReactCompositeComponent.Mixin.mountComponent.call(this, rootID, transaction, context); + + if (!result) { + this._stateless = true; + // Will throw error if element isn't stateless + result = ReactStatelessComponent.Mixin.mountComponent.call(this, rootID, transaction, context); + } + + return result; + }, + + receiveComponent: function(nextElement, transaction, nextContext) { + if (this._stateless) { + return ReactStatelessComponent.Mixin.receiveComponent.call(this, nextElement, transaction, nextContext); + } else { + return ReactCompositeComponent.Mixin.receiveComponent.call(this, nextElement, transaction, nextContext); + } + }, + + unmountComponent: function() { + if (this._stateless) { + return ReactStatelessComponent.Mixin.unmountComponent.call(this); + } else { + return ReactCompositeComponent.Mixin.unmountComponent.call(this); + } + }, + + performUpdateIfNecessary: function(transaction) { + if (this._stateless) { + return ReactStatelessComponent.Mixin.performUpdateIfNecessary.call(this, transaction); + } else { + return ReactCompositeComponent.Mixin.performUpdateIfNecessary.call(this, transaction); + } + }, + + getName: function() { + if (this._stateless) { + return ReactStatelessComponent.Mixin.getName.call(this); + } else { + return ReactCompositeComponent.Mixin.getName.call(this); + } + }, + + updateComponent: function(a, b, c, d, e) { + if (this._stateless) { + return ReactStatelessComponent.Mixin.updateComponent.call(this, a, b, c, d, e); + } else { + return ReactCompositeComponent.Mixin.updateComponent.call(this, a, b, c, d, e); + } + }, + + _renderValidatedComponent: function() { + if (this._stateless) { + return ReactStatelessComponent.Mixin._renderValidatedComponent.call(this); + } else { + return ReactCompositeComponent.Mixin._renderValidatedComponent.call(this); + } + }, } -); +) /** * Check if the type reference is a known internal type. I.e. not a user @@ -61,7 +127,7 @@ function isInternalComponentType(type) { * @param {function} type * @return {boolean} Returns true if this is a stateless function type. */ -function isStatelessComponentType(type) { +function isPropablyStatelessComponentType(type) { return ( typeof type === 'function' && (typeof type.prototype === 'undefined' || @@ -106,8 +172,8 @@ function instantiateReactComponent(node, parentCompositeType) { // represenations. I.e. ART. Once those are updated to use the string // representation, we can drop this code path. instance = new element.type(element); - } else if (isStatelessComponentType(element.type)) { - instance = new ReactStatelessComponentWrapper(); + } else if (isPropablyStatelessComponentType(element.type)) { + instance = new ReactStatelessOrCompositeComponentWrapper(); } else { instance = new ReactCompositeComponentWrapper(); } From 1cca5955a814a27ec7744b052977819b56bdda5d Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Thu, 2 Jul 2015 15:54:55 +0300 Subject: [PATCH 17/17] WIP: Typo --- src/renderers/shared/reconciler/instantiateReactComponent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js index d2c06e5d9349c..b726ba32f8904 100644 --- a/src/renderers/shared/reconciler/instantiateReactComponent.js +++ b/src/renderers/shared/reconciler/instantiateReactComponent.js @@ -127,7 +127,7 @@ function isInternalComponentType(type) { * @param {function} type * @return {boolean} Returns true if this is a stateless function type. */ -function isPropablyStatelessComponentType(type) { +function isProbablyStatelessComponentType(type) { return ( typeof type === 'function' && (typeof type.prototype === 'undefined' ||