Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 12 additions & 42 deletions src/isomorphic/classic/element/ReactElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,37 +49,6 @@ var loggedTypeFailures = {};

var NUMERIC_PROPERTY_REGEX = /^\d+$/;

/**
* Gets the instance's name for use in warnings.
*
* @internal
* @return {?string} Display name or undefined
*/
function getName(instance) {
var publicInstance = instance && instance.getPublicInstance();
if (!publicInstance) {
return undefined;
}
var constructor = publicInstance.constructor;
if (!constructor) {
return undefined;
}
return constructor.displayName || constructor.name || undefined;
}

/**
* Gets the current owner's displayName for use in warnings.
*
* @internal
* @return {?string} Display name or undefined
*/
function getCurrentOwnerDisplayName() {
var current = ReactCurrentOwner.current;
return (
current && getName(current) || undefined
);
}

/**
* Warn if the element doesn't have an explicit key assigned to it.
* This element is in an array. The array could grow and shrink or be
Expand Down Expand Up @@ -150,24 +119,25 @@ function validatePropertyKey(name, element, parentType) {
* if the warning has already been shown before (and shouldn't be shown again).
*/
function getAddendaForKeyUse(messageType, element, parentType) {
var ownerName = getCurrentOwnerDisplayName();
var parentName = typeof parentType === 'string' ?
parentType : parentType.displayName || parentType.name;
var addendum = getDeclarationErrorAddendum();
if (!addendum) {
var parentName = typeof parentType === 'string' ?
parentType : parentType.displayName || parentType.name;
if (parentName) {
addendum = ` Check the React.render call using <${parentName}>.`;
}
}

var useName = ownerName || parentName;
var memoizer = ownerHasKeyUseWarning[messageType] || (
ownerHasKeyUseWarning[messageType] = {}
);
if (memoizer[useName]) {
if (memoizer[addendum]) {
return null;
}
memoizer[useName] = true;
memoizer[addendum] = true;

var addenda = {
parentOrOwner:
ownerName ? ` Check the render method of ${ownerName}.` :
parentName ? ` Check the React.render call using <${parentName}>.` :
null,
parentOrOwner: addendum,
url: ' See https://fb.me/react-warning-keys for more information.',
childOwner: null,
};
Expand All @@ -180,7 +150,7 @@ function getAddendaForKeyUse(messageType, element, parentType) {
element._owner !== ReactCurrentOwner.current) {
// Give the component that originally created this child.
addenda.childOwner =
` It was passed a child from ${getName(element._owner)}.`;
` It was passed a child from ${element._owner.getName()}.`;
}

return addenda;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('ReactElementValidator', function() {
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'Each child in an array or iterator should have a unique "key" prop. ' +
'Check the render method of InnerClass. ' +
'Check the render method of `InnerClass`. ' +
'It was passed a child from ComponentWrapper. '
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('ReactJSXElementValidator', function() {
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'Each child in an array or iterator should have a unique "key" prop. ' +
'Check the render method of InnerComponent. ' +
'Check the render method of `InnerComponent`. ' +
'It was passed a child from ComponentWrapper. '
);
});
Expand Down
39 changes: 36 additions & 3 deletions src/renderers/shared/reconciler/ReactCompositeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ function getDeclarationErrorAddendum(component) {
return '';
}

function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
var Component = ReactInstanceMap.get(this)._currentElement.type;
return new Component(this.props, this.context, this.updater);
};

/**
* ------------------ The Life-Cycle of a Composite Component ------------------
*
Expand Down Expand Up @@ -126,7 +133,24 @@ var ReactCompositeComponentMixin = {
var Component = this._currentElement.type;

// Initialize the public class
var inst = new Component(publicProps, publicContext, ReactUpdateQueue);
var inst;
var renderedElement;

if (__DEV__) {
ReactCurrentOwner.current = this;
try {
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)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work because the construct trap will intercept any non-object and turn it into this. :(

renderedElement = inst;
inst = new StatelessComponent(Component);
}

if (__DEV__) {
// This will throw later in _renderValidatedComponent, but add an early
Expand Down Expand Up @@ -231,7 +255,10 @@ var ReactCompositeComponentMixin = {
}
}

var renderedElement = this._renderValidatedComponent();
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent();
}

this._renderedComponent = this._instantiateReactComponent(
renderedElement
Expand Down Expand Up @@ -265,6 +292,7 @@ var ReactCompositeComponentMixin = {

ReactReconciler.unmountComponent(this._renderedComponent);
this._renderedComponent = null;
this._instance = null;

// Reset pending fields
// Even if this component is scheduled for another update in ReactUpdates,
Expand Down Expand Up @@ -759,6 +787,7 @@ var ReactCompositeComponentMixin = {
*/
attachRef: function(ref, component) {
var inst = this.getPublicInstance();
invariant(inst != null, 'Stateless function components cannot have refs.');
var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
refs[ref] = component.getPublicInstance();
},
Expand Down Expand Up @@ -800,7 +829,11 @@ var ReactCompositeComponentMixin = {
* @internal
*/
getPublicInstance: function() {
return this._instance;
var inst = this._instance;
if (inst instanceof StatelessComponent) {
return null;
}
return inst;
},

// Stub
Expand Down
20 changes: 10 additions & 10 deletions src/renderers/shared/reconciler/ReactUpdateQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,6 @@ function enqueueUpdate(internalInstance) {
}

function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
if (__DEV__) {
warning(
ReactCurrentOwner.current == null,
'%s(...): Cannot update during an existing state transition ' +
'(such as within `render`). Render methods should be a pure function ' +
'of props and state.',
callerName
);
}

var internalInstance = ReactInstanceMap.get(publicInstance);
if (!internalInstance) {
if (__DEV__) {
Expand All @@ -54,6 +44,16 @@ function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
return null;
}

if (__DEV__) {
warning(
ReactCurrentOwner.current == null,
'%s(...): Cannot update during an existing state transition ' +
'(such as within `render`). Render methods should be a pure function ' +
'of props and state.',
callerName
);
}

return internalInstance;
}

Expand Down
Loading