@samitouri / QOS-React / commits / 291379c272

Elements from functional components in TestUtils should have no owner

Fixes #5292 and #6194

Dan Abramov committed Mar 28, 2016 at 21:05 UTC 291379c27278cf1bde1a16e2184e750cd1a15a92
3 files changed +62 -36
src/renderers/shared/reconciler/ReactCompositeComponent.js
+40 -36
@@ -61,6 +61,10 @@ function warnIfInvalidElement(Component, element) {
61 }
62 }
63
64 +function shouldConstruct(Component) {
65 + return Component.prototype && Component.prototype.isReactComponent;
66 +}
67 +
68 /**
69 * ------------------ The Life-Cycle of a Composite Component ------------------
70 *
@@ -159,44 +163,22 @@ var ReactCompositeComponentMixin = {
163 var Component = this._currentElement.type;
164
165 // Initialize the public class
162 - var inst;
166 + var inst = this._constructComponent(publicProps, publicContext);
167 var renderedElement;
168
165 - if (Component.prototype && Component.prototype.isReactComponent) {
166 - if (__DEV__) {
167 - ReactCurrentOwner.current = this;
168 - try {
169 - inst = new Component(publicProps, publicContext, ReactUpdateQueue);
170 - } finally {
171 - ReactCurrentOwner.current = null;
172 - }
173 - } else {
174 - inst = new Component(publicProps, publicContext, ReactUpdateQueue);
175 - }
176 - } else {
177 - if (__DEV__) {
178 - ReactCurrentOwner.current = this;
179 - try {
180 - inst = Component(publicProps, publicContext, ReactUpdateQueue);
181 - } finally {
182 - ReactCurrentOwner.current = null;
183 - }
184 - } else {
185 - inst = Component(publicProps, publicContext, ReactUpdateQueue);
186 - }
187 - if (inst == null || inst.render == null) {
188 - renderedElement = inst;
189 - warnIfInvalidElement(Component, renderedElement);
190 - invariant(
191 - inst === null ||
192 - inst === false ||
193 - ReactElement.isValidElement(inst),
194 - '%s(...): A valid React element (or null) must be returned. You may have ' +
195 - 'returned undefined, an array or some other invalid object.',
196 - Component.displayName || Component.name || 'Component'
197 - );
198 - inst = new StatelessComponent(Component);
199 - }
169 + // Support functional components
170 + if (!shouldConstruct(Component) && (inst == null || inst.render == null)) {
171 + renderedElement = inst;
172 + warnIfInvalidElement(Component, renderedElement);
173 + invariant(
174 + inst === null ||
175 + inst === false ||
176 + ReactElement.isValidElement(inst),
177 + '%s(...): A valid React element (or null) must be returned. You may have ' +
178 + 'returned undefined, an array or some other invalid object.',
179 + Component.displayName || Component.name || 'Component'
180 + );
181 + inst = new StatelessComponent(Component);
182 }
183
184 if (__DEV__) {
@@ -324,6 +306,28 @@ var ReactCompositeComponentMixin = {
306 return markup;
307 },
308
309 + _constructComponent: function(publicProps, publicContext) {
310 + if (__DEV__) {
311 + ReactCurrentOwner.current = this;
312 + try {
313 + return this._constructComponentWithoutOwner(publicProps, publicContext);
314 + } finally {
315 + ReactCurrentOwner.current = null;
316 + }
317 + } else {
318 + return this._constructComponentWithoutOwner(publicProps, publicContext);
319 + }
320 + },
321 +
322 + _constructComponentWithoutOwner: function(publicProps, publicContext) {
323 + var Component = this._currentElement.type;
324 + if (shouldConstruct(Component)) {
325 + return new Component(publicProps, publicContext, ReactUpdateQueue);
326 + } else {
327 + return Component(publicProps, publicContext, ReactUpdateQueue);
328 + }
329 + },
330 +
331 performInitialMountWithErrorHandling: function(
332 renderedElement,
333 nativeParent,
src/test/ReactTestUtils.js
+2
@@ -399,6 +399,8 @@ var ShallowComponentWrapper = function(element) {
399 assign(
400 ShallowComponentWrapper.prototype,
401 ReactCompositeComponent.Mixin, {
402 + _constructComponent:
403 + ReactCompositeComponent.Mixin._constructComponentWithoutOwner,
404 _instantiateReactComponent: function(element) {
405 return new NoopInternalComponent(element);
406 },
src/test/__tests__/ReactTestUtils-test.js
+20
@@ -47,6 +47,26 @@ describe('ReactTestUtils', function() {
47 ]);
48 });
49
50 + it('should shallow render a functional component', function() {
51 + function SomeComponent() {
52 + return (
53 + <div>
54 + <span className="child1" />
55 + <span className="child2" />
56 + </div>
57 + );
58 + }
59 +
60 + var shallowRenderer = ReactTestUtils.createRenderer();
61 + var result = shallowRenderer.render(<SomeComponent />);
62 +
63 + expect(result.type).toBe('div');
64 + expect(result.props.children).toEqual([
65 + <span className="child1" />,
66 + <span className="child2" />,
67 + ]);
68 + });
69 +
70 it('should throw for invalid elements', function() {
71 var SomeComponent = React.createClass({
72 render: function() {