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() {