Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moar Warnings #3171

Merged
merged 3 commits into from
Feb 17, 2015
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
1 change: 1 addition & 0 deletions src/browser/__tests__/ReactDOM-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,5 @@ describe('ReactDOM', function() {
expect(element.type).toBe('div');
expect(console.warn.argsForCall.length).toBe(0);
});

});
18 changes: 18 additions & 0 deletions src/browser/findDOMNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
*/

'use strict';

var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactMount = require('ReactMount');

var invariant = require('invariant');
var isNode = require('isNode');
var warning = require('warning');

/**
* Returns the DOM node rendered by this element.
Expand All @@ -24,6 +27,21 @@ var isNode = require('isNode');
* @return {DOMElement} The root node of this element.
*/
function findDOMNode(componentOrElement) {
if (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
warning(
owner._warnedAboutRefsInRender,
'%s is accessing getDOMNode or findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
owner.getName() || 'A component'
);
owner._warnedAboutRefsInRender = true;
}
}
if (componentOrElement == null) {
return null;
}
Expand Down
16 changes: 16 additions & 0 deletions src/classic/class/ReactClass.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'use strict';

var ReactComponent = require('ReactComponent');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement');
var ReactErrorUtils = require('ReactErrorUtils');
var ReactInstanceMap = require('ReactInstanceMap');
Expand Down Expand Up @@ -746,6 +747,21 @@ var ReactClassMixin = {
* @final
*/
isMounted: function() {
if (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
warning(
owner._warnedAboutRefsInRender,
'%s is accessing isMounted inside its render() function. ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
owner.getName() || 'A component'
);
owner._warnedAboutRefsInRender = true;
}
}
var internalInstance = ReactInstanceMap.get(this);
return (
internalInstance &&
Expand Down
27 changes: 27 additions & 0 deletions src/classic/element/ReactElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,33 @@ var ReactElementValidator = {
);
// Legacy hook TODO: Warn if this is accessed
validatedFactory.type = type;

if (__DEV__) {
try {
Object.defineProperty(
validatedFactory,
'type',
{
enumerable: false,
get: function() {
warning(
false,
'Factory.type is deprecated. Access the class directly ' +
'before passing it to createFactory.'
);
Object.defineProperty(this, 'type', {
value: type
});
return type;
}
}
);
} catch (x) {
// IE will fail on defineProperty (es5-shim/sham too)
}
}


return validatedFactory;
}

Expand Down
19 changes: 19 additions & 0 deletions src/classic/element/__tests__/ReactElementValidator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,23 @@ describe('ReactElementValidator', function() {
expect(console.warn.calls[0].args[0]).toContain('use of a keyed object');
});

it('should warn when accessing .type on an element factory', function() {
spyOn(console, 'warn');
var TestComponent = React.createClass({
render: function() {
return <div />;
}
});
var TestFactory = React.createFactory(TestComponent);
expect(TestFactory.type).toBe(TestComponent);
expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toBe(
'Warning: Factory.type is deprecated. Access the class directly before ' +
'passing it to createFactory.'
);
// Warn once, not again
expect(TestFactory.type).toBe(TestComponent);
expect(console.warn.argsForCall.length).toBe(1);
});

});
20 changes: 0 additions & 20 deletions src/core/__tests__/ReactComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,26 +254,6 @@ describe('ReactComponent', function() {
]);
});

it('should correctly determine if a component is mounted', function() {
var Component = React.createClass({
componentWillMount: function() {
expect(this.isMounted()).toBeFalsy();
},
componentDidMount: function() {
expect(this.isMounted()).toBeTruthy();
},
render: function() {
expect(this.isMounted()).toBeFalsy()
return <div/>;
}
});

var element = <Component />;

var instance = ReactTestUtils.renderIntoDocument(element);
expect(instance.isMounted()).toBeTruthy();
});

it('fires the callback after a component is rendered', function() {
var callback = mocks.getMockFunction();
var container = document.createElement('div');
Expand Down
52 changes: 45 additions & 7 deletions src/core/__tests__/ReactComponentLifeCycle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,54 @@ describe('ReactComponentLifeCycle', function() {
);
});

it('is not mounted inside initial render', function() {
var InitialRender = React.createClass({
it('should correctly determine if a component is mounted', function() {
spyOn(console, 'warn');
var Component = React.createClass({
componentWillMount: function() {
expect(this.isMounted()).toBeFalsy();
},
componentDidMount: function() {
expect(this.isMounted()).toBeTruthy();
},
render: function() {
expect(this.isMounted()).toBe(false);
return (
<div></div>
);
expect(this.isMounted()).toBeFalsy()
return <div/>;
}
});
ReactTestUtils.renderIntoDocument(<InitialRender />);

var element = <Component />;

var instance = ReactTestUtils.renderIntoDocument(element);
expect(instance.isMounted()).toBeTruthy();

expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain(
'Component is accessing isMounted inside its render()'
);
});

it('warns if getDOMNode is used inside render', function() {
spyOn(console, 'warn');
var Component = React.createClass({
getInitialState: function() {
return { isMounted: false };
},
componentDidMount: function() {
this.setState({ isMounted: true });
},
render: function() {
if (this.state.isMounted) {
expect(this.getDOMNode().tagName).toBe('DIV');
}
return <div/>;
}
});

ReactTestUtils.renderIntoDocument(<Component />);
expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain(
'Component is accessing getDOMNode or findDOMNode inside its render()'
);
});

it('should carry through each of the phases of setup', function() {
Expand Down
1 change: 1 addition & 0 deletions src/core/instantiateReactComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function instantiateReactComponent(node, parentCompositeType) {

if (__DEV__) {
instance._isOwnerNecessary = false;
instance._warnedAboutRefsInRender = false;
}

// Internal instances should fully constructed at this point, so they should
Expand Down
9 changes: 9 additions & 0 deletions src/utils/__tests__/traverseAllChildren-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ describe('traverseAllChildren', function() {
});

it('should use keys from entry iterables', function() {
spyOn(console, 'warn');

var threeDivEntryIterable = {
'@@iterator': function() {
var i = 0;
Expand Down Expand Up @@ -445,6 +447,13 @@ describe('traverseAllChildren', function() {
'.$#3:0',
2
);

expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain(
'Warning: Using Maps as children is not yet fully supported. It is an ' +
'experimental feature that might be removed. Convert it to a sequence ' +
'/ iterable of keyed ReactElements instead.'
);
});

});
12 changes: 12 additions & 0 deletions src/utils/traverseAllChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var ReactInstanceHandles = require('ReactInstanceHandles');

var getIteratorFn = require('getIteratorFn');
var invariant = require('invariant');
var warning = require('warning');

var SEPARATOR = ReactInstanceHandles.SEPARATOR;
var SUBSEPARATOR = ':';
Expand All @@ -34,6 +35,8 @@ var userProvidedKeyEscaperLookup = {

var userProvidedKeyEscapeRegex = /[=.:]/g;

var didWarnAboutMaps = false;

function userProvidedKeyEscaper(match) {
return userProvidedKeyEscaperLookup[match];
}
Expand Down Expand Up @@ -158,6 +161,15 @@ function traverseAllChildrenImpl(
);
}
} else {
if (__DEV__) {
warning(
didWarnAboutMaps,
'Using Maps as children is not yet fully supported. It is an ' +
'experimental feature that might be removed. Convert it to a ' +
'sequence / iterable of keyed ReactElements instead.'
);
didWarnAboutMaps = true;
}
// Iterator will provide entry [k,v] tuples rather than values.
while (!(step = iterator.next()).done) {
var entry = step.value;
Expand Down