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

A more descriptive createElement warning #7402

Closed
wants to merge 10 commits into from
91 changes: 91 additions & 0 deletions docs/warnings/invalid-element-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: Invalid Element Type Warning
layout: single
permalink: warnings/invalid-element-type.html
---

You probably came here because your code is trying to create a ReactElement with an invalid type using JSX or the `React.createElement` API. This usually happens when you have an invalid import statement.
Copy link
Collaborator

Choose a reason for hiding this comment

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

The last sentence is only true if you have undefined type.


`React.createElement` requires an argument of type `string` (e.g. 'div', 'span'), or a `ReactClass`/`React.Component`. It cannot be of type `number`, `boolean`, `undefined` or `null`. See the documentation of this API: [https://facebook.github.io/react/docs/top-level-api.html](https://facebook.github.io/react/docs/top-level-api.html)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's also avoid saying ReactClass/React.Component here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Which API are you referring to? It would make sense to link to createElement() specifically. You can hover over its header in docs to see the exact anchor link.

Please also remove domain from the URL and just start it with /react. This is useful in case docs move.

Copy link
Author

Choose a reason for hiding this comment

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

I was referring to the createElement API. Not sure why I didn't see the hash button for the permalink, will fix!


The following common examples will trigger this error:

### Invalid types

Ensure that your component is not of the following types: undefined, boolean, number or null.

`Components.js`

```js
let Foo, Bar;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's avoid let here and use var so people don't think this warning is somehow related to let.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also let's avoid Foo and Bar and use something more realistic like Button.


if (false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's not clear why you have if (false). Could you come up with a more realistic example please?

Foo = () => <div />;
Copy link
Collaborator

Choose a reason for hiding this comment

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

People reading this might not understand arrow functions. I would avoid them here.

}

Bar = React.createElement(42);
// The following types are invalid, too.
// Bar = 42;
// Bar = null;
// Bar = undefined;
// Bar = true;

export { Foo, Bar }; // Foo is undefined and Bar is an invalid element.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why would somebody want to export an element from a file? This looks confusing.

```

`App.js`

```js
import { Foo, Bar } from './Components'

class ReactApp extends Component {
render() {
return <Foo />; // or return <Bar />
}
}
```

### Invalid member imports

This happens when attempting to import a member as a default member, or importing a default member as a member.

`Components.js`

```js
export const Foo = () => { return <div /> }
```

`App.js`

```js
import Foo from './Components' // wrong!
// correct: import { Foo } from './Components';
Copy link
Collaborator

Choose a reason for hiding this comment

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

People will misunderstand this and think curly braces are always necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe just add a // not a default export comment before the export const Foo line?


class ReactApp extends Component {
render() {
return <Foo />;
}
}
```

### Invalid or missing export

Check that the component is exported properly with the keyword `export`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

People might be using CommonJS so this is not always the case.

Copy link
Contributor

Choose a reason for hiding this comment

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

"or your module system's export mechanism"?

The documentation either has to pick a canonical modules system, or it'd need use cases for ES6, commonjs, AMD...


`Components.js`

```js
const Foo = () => { return <div /> } // Foo needs to be exported
```

`App.js`

```js
import { Foo } from './Components' // Foo is undefined

class ReactApp extends Component {
render() {
return <Foo />;
}
}
```
34 changes: 25 additions & 9 deletions src/isomorphic/classic/element/ReactElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,21 +180,37 @@ function validatePropTypes(element) {
}
}

function warnInvalidType(inputValue) {
if (inputValue === undefined) {
warning(
false,
'React.createElement: undefined is an invalid element type. ' +
'Did you mistype an import or forget to export your component? ' +
'It should be a string (for DOM elements), component ' +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unnecessary extra indentation here and below. Let's keep lines aligned.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is useful, let's keep it.

'class or function (for user-defined components).' +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add "a" before "component class" and before "function".

'%s See https://fb.me/react-invalid-element-type for more information.',
getDeclarationErrorAddendum()
);
} else {
warning(
false,
'React.createElement: %s is an invalid element type. ' +
'It should be a string (for DOM elements), component ' +
'class or function (for user-defined components).' +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here.

'%s See https://fb.me/react-invalid-element-type for more information.',
inputValue,
getDeclarationErrorAddendum()
);
}
}

var ReactElementValidator = {

createElement: function(type, props, children) {
var validType = typeof type === 'string' || typeof type === 'function' ||
(type !== null && typeof type === 'object');
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
if (!validType) {
warning(
false,
'React.createElement: type should not be null, undefined, boolean, or ' +
'number. It should be a string (for DOM elements) or a ReactClass ' +
'(for composite components).%s',
getDeclarationErrorAddendum()
);
warnInvalidType(type);
}

var element = ReactElement.createElement.apply(this, arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,24 +297,29 @@ describe('ReactElementValidator', function() {
React.createElement(123);
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: undefined is an invalid element type. ' +
'Did you mistype an import or forget to export your component? ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: null is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
expect(console.error.calls.argsFor(2)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: true is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
expect(console.error.calls.argsFor(3)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: 123 is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
React.createElement('div');
expect(console.error.calls.count()).toBe(4);
Expand All @@ -336,10 +341,10 @@ describe('ReactElementValidator', function() {
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components). Check the render method of ' +
'`ParentComp`.'
'Warning: React.createElement: null is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). Check the render method of `ParentComp`. ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
});

Expand Down Expand Up @@ -537,9 +542,11 @@ describe('ReactElementValidator', function() {
void <Foo>{[<div />]}</Foo>;
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: undefined is an invalid element type. ' +
'Did you mistype an import or forget to export your component? ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). ' +
'See https://fb.me/react-invalid-element-type for more information.'
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,25 @@ describe('ReactJSXElementValidator', function() {
void <Num />;
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
'undefined is an invalid element type. ' +
'Did you mistype an import or forget to export your component? ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). '
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
'null is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). '
);
expect(console.error.calls.argsFor(2)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
'true is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). '
);
expect(console.error.calls.argsFor(3)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
'123 is an invalid element type. ' +
'It should be a string (for DOM elements), component class or function ' +
'(for user-defined components). '
);
void <Div />;
expect(console.error.calls.count()).toBe(4);
Expand Down