diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index 8bfbdcbf55..b96ece5133 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -42,7 +42,7 @@ function StatelessComponent(Component) { } StatelessComponent.prototype.render = function() { var Component = ReactInstanceMap.get(this)._currentElement.type; - return new Component(this.props, this.context, this.updater); + return Component(this.props, this.context, this.updater); }; /** @@ -136,18 +136,26 @@ var ReactCompositeComponentMixin = { var inst; var renderedElement; - if (__DEV__) { - ReactCurrentOwner.current = this; - try { + // This is a way to detect if Component is a stateless arrow function + // component, which is not newable. It might not be 100% reliable but is + // something we can do until we start detecting that Component extends + // React.Component. We already assume that typeof Component === 'function'. + var canInstantiate = 'prototype' in Component; + + if (canInstantiate) { + if (__DEV__) { + ReactCurrentOwner.current = this; + try { + inst = new Component(publicProps, publicContext, ReactUpdateQueue); + } finally { + ReactCurrentOwner.current = null; + } + } else { inst = new Component(publicProps, publicContext, ReactUpdateQueue); - } finally { - ReactCurrentOwner.current = null; } - } else { - inst = new Component(publicProps, publicContext, ReactUpdateQueue); } - if (inst === null || inst === false || ReactElement.isValidElement(inst)) { + if (!canInstantiate || inst === null || inst === false || ReactElement.isValidElement(inst)) { renderedElement = inst; inst = new StatelessComponent(Component); } @@ -168,7 +176,9 @@ var ReactCompositeComponentMixin = { // We support ES6 inheriting from React.Component, the module pattern, // and stateless components, but not ES6 classes that don't extend warning( - Component.isReactClass || !(inst instanceof Component), + Component.isReactClass || + !canInstantiate || + !(inst instanceof Component), '%s(...): React component classes must extend React.Component.', Component.displayName || Component.name || 'Component' ); diff --git a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js index 58e359d8bc..c94c60f827 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js @@ -183,4 +183,19 @@ describe('ReactStatelessComponent', function() { ReactDOM.render(, el); expect(el.textContent).toBe('en'); }); + + it('should work with arrow functions', function() { + // TODO: actually use arrow functions, probably need node v4 and maybe + // a separate file that we blacklist from the arrow function transform. + // We can't actually test this without native arrow functions since the + // issues (non-newable) don't apply to any other functions. + var Child = function() { + return
; + }; + // Will create a new bound function without a prototype, much like a native + // arrow function. + Child = Child.bind(this); + + expect(() => ReactTestUtils.renderIntoDocument()).not.toThrow(); + }); });