Skip to content

Commit d1a2193

Browse files
committed
Merge pull request #4587 from spicyj/stateless-fn
Minimal implementation of stateless components
2 parents 37ee03f + 5a7bd96 commit d1a2193

File tree

6 files changed

+244
-57
lines changed

6 files changed

+244
-57
lines changed

src/isomorphic/classic/element/ReactElementValidator.js

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -49,37 +49,6 @@ var loggedTypeFailures = {};
4949

5050
var NUMERIC_PROPERTY_REGEX = /^\d+$/;
5151

52-
/**
53-
* Gets the instance's name for use in warnings.
54-
*
55-
* @internal
56-
* @return {?string} Display name or undefined
57-
*/
58-
function getName(instance) {
59-
var publicInstance = instance && instance.getPublicInstance();
60-
if (!publicInstance) {
61-
return undefined;
62-
}
63-
var constructor = publicInstance.constructor;
64-
if (!constructor) {
65-
return undefined;
66-
}
67-
return constructor.displayName || constructor.name || undefined;
68-
}
69-
70-
/**
71-
* Gets the current owner's displayName for use in warnings.
72-
*
73-
* @internal
74-
* @return {?string} Display name or undefined
75-
*/
76-
function getCurrentOwnerDisplayName() {
77-
var current = ReactCurrentOwner.current;
78-
return (
79-
current && getName(current) || undefined
80-
);
81-
}
82-
8352
/**
8453
* Warn if the element doesn't have an explicit key assigned to it.
8554
* This element is in an array. The array could grow and shrink or be
@@ -150,24 +119,25 @@ function validatePropertyKey(name, element, parentType) {
150119
* if the warning has already been shown before (and shouldn't be shown again).
151120
*/
152121
function getAddendaForKeyUse(messageType, element, parentType) {
153-
var ownerName = getCurrentOwnerDisplayName();
154-
var parentName = typeof parentType === 'string' ?
155-
parentType : parentType.displayName || parentType.name;
122+
var addendum = getDeclarationErrorAddendum();
123+
if (!addendum) {
124+
var parentName = typeof parentType === 'string' ?
125+
parentType : parentType.displayName || parentType.name;
126+
if (parentName) {
127+
addendum = ` Check the React.render call using <${parentName}>.`;
128+
}
129+
}
156130

157-
var useName = ownerName || parentName;
158131
var memoizer = ownerHasKeyUseWarning[messageType] || (
159132
ownerHasKeyUseWarning[messageType] = {}
160133
);
161-
if (memoizer[useName]) {
134+
if (memoizer[addendum]) {
162135
return null;
163136
}
164-
memoizer[useName] = true;
137+
memoizer[addendum] = true;
165138

166139
var addenda = {
167-
parentOrOwner:
168-
ownerName ? ` Check the render method of ${ownerName}.` :
169-
parentName ? ` Check the React.render call using <${parentName}>.` :
170-
null,
140+
parentOrOwner: addendum,
171141
url: ' See https://fb.me/react-warning-keys for more information.',
172142
childOwner: null,
173143
};
@@ -180,7 +150,7 @@ function getAddendaForKeyUse(messageType, element, parentType) {
180150
element._owner !== ReactCurrentOwner.current) {
181151
// Give the component that originally created this child.
182152
addenda.childOwner =
183-
` It was passed a child from ${getName(element._owner)}.`;
153+
` It was passed a child from ${element._owner.getName()}.`;
184154
}
185155

186156
return addenda;

src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ describe('ReactElementValidator', function() {
7979
expect(console.error.argsForCall.length).toBe(1);
8080
expect(console.error.argsForCall[0][0]).toContain(
8181
'Each child in an array or iterator should have a unique "key" prop. ' +
82-
'Check the render method of InnerClass. ' +
82+
'Check the render method of `InnerClass`. ' +
8383
'It was passed a child from ComponentWrapper. '
8484
);
8585
});

src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ describe('ReactJSXElementValidator', function() {
8383
expect(console.error.argsForCall.length).toBe(1);
8484
expect(console.error.argsForCall[0][0]).toContain(
8585
'Each child in an array or iterator should have a unique "key" prop. ' +
86-
'Check the render method of InnerComponent. ' +
86+
'Check the render method of `InnerComponent`. ' +
8787
'It was passed a child from ComponentWrapper. '
8888
);
8989
});

src/renderers/shared/reconciler/ReactCompositeComponent.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ function getDeclarationErrorAddendum(component) {
3838
return '';
3939
}
4040

41+
function StatelessComponent(Component) {
42+
}
43+
StatelessComponent.prototype.render = function() {
44+
var Component = ReactInstanceMap.get(this)._currentElement.type;
45+
return new Component(this.props, this.context, this.updater);
46+
};
47+
4148
/**
4249
* ------------------ The Life-Cycle of a Composite Component ------------------
4350
*
@@ -126,7 +133,24 @@ var ReactCompositeComponentMixin = {
126133
var Component = this._currentElement.type;
127134

128135
// Initialize the public class
129-
var inst = new Component(publicProps, publicContext, ReactUpdateQueue);
136+
var inst;
137+
var renderedElement;
138+
139+
if (__DEV__) {
140+
ReactCurrentOwner.current = this;
141+
try {
142+
inst = new Component(publicProps, publicContext, ReactUpdateQueue);
143+
} finally {
144+
ReactCurrentOwner.current = null;
145+
}
146+
} else {
147+
inst = new Component(publicProps, publicContext, ReactUpdateQueue);
148+
}
149+
150+
if (inst === null || inst === false || ReactElement.isValidElement(inst)) {
151+
renderedElement = inst;
152+
inst = new StatelessComponent(Component);
153+
}
130154

131155
if (__DEV__) {
132156
// This will throw later in _renderValidatedComponent, but add an early
@@ -231,7 +255,10 @@ var ReactCompositeComponentMixin = {
231255
}
232256
}
233257

234-
var renderedElement = this._renderValidatedComponent();
258+
// If not a stateless component, we now render
259+
if (renderedElement === undefined) {
260+
renderedElement = this._renderValidatedComponent();
261+
}
235262

236263
this._renderedComponent = this._instantiateReactComponent(
237264
renderedElement
@@ -265,6 +292,7 @@ var ReactCompositeComponentMixin = {
265292

266293
ReactReconciler.unmountComponent(this._renderedComponent);
267294
this._renderedComponent = null;
295+
this._instance = null;
268296

269297
// Reset pending fields
270298
// Even if this component is scheduled for another update in ReactUpdates,
@@ -759,6 +787,7 @@ var ReactCompositeComponentMixin = {
759787
*/
760788
attachRef: function(ref, component) {
761789
var inst = this.getPublicInstance();
790+
invariant(inst != null, 'Stateless function components cannot have refs.');
762791
var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
763792
refs[ref] = component.getPublicInstance();
764793
},
@@ -800,7 +829,11 @@ var ReactCompositeComponentMixin = {
800829
* @internal
801830
*/
802831
getPublicInstance: function() {
803-
return this._instance;
832+
var inst = this._instance;
833+
if (inst instanceof StatelessComponent) {
834+
return null;
835+
}
836+
return inst;
804837
},
805838

806839
// Stub

src/renderers/shared/reconciler/ReactUpdateQueue.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,6 @@ function enqueueUpdate(internalInstance) {
2525
}
2626

2727
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
28-
if (__DEV__) {
29-
warning(
30-
ReactCurrentOwner.current == null,
31-
'%s(...): Cannot update during an existing state transition ' +
32-
'(such as within `render`). Render methods should be a pure function ' +
33-
'of props and state.',
34-
callerName
35-
);
36-
}
37-
3828
var internalInstance = ReactInstanceMap.get(publicInstance);
3929
if (!internalInstance) {
4030
if (__DEV__) {
@@ -54,6 +44,16 @@ function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
5444
return null;
5545
}
5646

47+
if (__DEV__) {
48+
warning(
49+
ReactCurrentOwner.current == null,
50+
'%s(...): Cannot update during an existing state transition ' +
51+
'(such as within `render`). Render methods should be a pure function ' +
52+
'of props and state.',
53+
callerName
54+
);
55+
}
56+
5757
return internalInstance;
5858
}
5959

0 commit comments

Comments
 (0)