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/create-element-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: Invalid type for createElement
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 call it Invalid Element Type Warning for consistency with other warning’s style. Same for filename, would be invalid-element-type.md.

layout: single
permalink: warnings/create-element-types.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.

`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)

The following common examples will trigger this error:

Copy link

@jamesblight jamesblight Aug 3, 2016

Choose a reason for hiding this comment

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

Most people will come to this page from the link provided in the warning message. Assuming that is true, I think a more informative message could be shown here without repeating the warning.

Instead of:

You probably came to this page because of this error:

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).

This usually occurs when attempting to render an element that is of an invalid type. The following examples will trigger this error:](url)

You could write something like:

You probably came here because your code is trying to create a ReactElement with an invalid type. This usually happens when you have an invalid import statement.

### Invalid types

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

`Components.js`

```js
let Foo, Bar;

if (false) {
Foo = () => <div />;
}

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.
```

`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';

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

### Invalid or missing export

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

`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 />;
}
}
```
52 changes: 40 additions & 12 deletions src/isomorphic/classic/element/ReactElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,46 @@ function validatePropTypes(element) {
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()
);
var validType = true;
var warnInvalidType = function(inputValue) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It can be expensive to create closures in such a hot path. Can you please hoist that function outside createElement()?

// warning(...) messages need to be string literals.
// If not, we can do a ternary expression to evaluate the need
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.

// for the mistyped import message, and not repeat the message
// twice here.
if (inputValue === undefined) {
warning(false,
'React.createElement: %s is an invalid value for a type. ' +
'It should be a string (for DOM elements), ReactClass or '+
'React.Component (for composite components). ' +
'Did you mistype an import or forget to export your component?' +
'%s See fb.me/react-warning-create-element for more information.',
inputValue, getDeclarationErrorAddendum()
);
} else {
warning(false,
'React.createElement: %s is an invalid value for a type. ' +
'It should be a string (for DOM elements), ReactClass or ' +
'React.Component (for composite components).' +
'%s See fb.me/react-warning-create-element for more information.',
inputValue, getDeclarationErrorAddendum()
);
}
};

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 remove this newline.

switch (typeof type) {
case 'string':
case 'function':
break;
case 'object':
if (type !== null) {
break;
}
// fallthrough if type is a null
Copy link
Collaborator

Choose a reason for hiding this comment

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

Fallthroughs are hard to understand. Can you please rewrite this to avoid it? Maybe replace switch with several boolean variables using each other.

default:
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
validType = false;
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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'Did you mistype an import or forget to export your component? ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). Check the render method of `ParentComp`. ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'Did you mistype an import or forget to export your component? ' +
'See fb.me/react-warning-create-element 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components). ' +
'Did you mistype an import or forget to export your component? '
);
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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite 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 value for a type. ' +
'It should be a string (for DOM elements), ReactClass or React.Component ' +
'(for composite components).'
);
void <Div />;
expect(console.error.calls.count()).toBe(4);
Expand Down