React.createElement: 类型无效 - 预期为字符串。

272

尝试让 react-router (v4.0.0) 和 react-hot-loader (3.0.0-beta.6) 协同工作,但是在浏览器控制台中出现以下错误:

Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite
components) but got: undefined. You likely forgot to export your
component from the file it's defined in.

index.js:

import React from 'react';
import ReactDom from 'react-dom';
import routes from './routes.js';
require('jquery');
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import './css/main.css';

const renderApp = (appRoutes) => {
    ReactDom.render(appRoutes, document.getElementById('root'));
};

renderApp( routes() );

路由.js:

import React from 'react';
import { AppContainer } from 'react-hot-loader';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import store from './store/store.js';
import { Provider } from 'react-redux';
import App from './containers/App.jsx';
import Products from './containers/shop/Products.jsx';
import Basket from './containers/shop/Basket.jsx';

const routes = () => (

    <AppContainer>
        <Provider store={store}>
            <Router history={browserHistory}>
                <Route path="/" component={App}>
                    <IndexRoute component={Products} />
                    <Route path="/basket" component={Basket} />
                </Route>
            </Router>
        </Provider>
    </AppContainer>

);

export default routes;

如果您使用 react-router-config,请确保使用 component 属性而不是 render,因为该软件包不支持后者。更多信息请参见 GitHub - totymedli
检查您是否已经创建了您要导入的组件,或者它实际上存在于项目目录中。 - Almuntasir Abir
48个回答

280
大多数情况下,这是由于错误的导出/导入造成的。

常见错误:

// File: LeComponent.js
export class LeComponent extends React.Component { ... }

// File: App.js
import LeComponent from './LeComponent';

可能选项:

// File: LeComponent.js 
export default class LeComponent extends React.Component { ... }

// File: App.js
import LeComponent from './LeComponent';
有一些可能导致错误的方式,但是这个错误有60%的概率是由于输入/输出不匹配引起的,每次都是如此。
编辑 通常情况下,您应该会得到一个堆栈跟踪,指示故障发生的大致位置。这通常紧随您在原始问题中提出的消息之后。
如果没有显示出来,可能值得调查一下为什么(可能是您丢失的构建设置)。无论如何,如果没有显示出来,唯一的行动方向是缩小输入/输出失败的范围。
不幸的是,如果没有堆栈跟踪,唯一的方法是手动删除每个模块/子模块,直到您不再收到错误消息,然后逐步向上回溯堆栈。 编辑2 通过评论,确实是一个导入问题,具体地说是导入一个不存在的模块。

2
你确定索引路由实际上是 RR4 的一部分吗?(我非常确定它不是) - Chris
嘿,JoeTidee - 如果你想继续使用RR4,请按照这个指南。我目前也处于同样的情况... https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/migrating.md - Ghan
3
遇到了使用import { Select } from 'react-select';的问题,将其更改为import Select from 'react-select'; - JJP
1
@PriyaRanjanSingh 仅凭一点评论无法确定问题。您能否发布一个带有详细信息的新问题? - Chris
1
对我来说有一个导入问题(在“默认导出”的导入上使用了括号)。很高兴问题如此简单……感谢帮助<3 - amota
显示剩余10条评论

62

我也曾遇到过这个错误。

我之前使用的是:

import BrowserRouter from 'react-router-dom';

解决方法是改成:

import { BrowserRouter } from 'react-router-dom';


3
我使用的是 import {AppLoading} from 'expo-app-loading' 而不是 import AppLoading from 'expo-app-loading',或者反过来。 - Bo rislav

17

试试这个

npm i react-router-dom@next

在您的App.js

import { BrowserRouter as Router, Route } from 'react-router-dom'

const Home = () => <h1>Home</h1>

const App = () =>(
  <Router>
    <Route path="/" component={Home} />
  </Router>
)

export default App;

你将如何向主组件发送/接收 props? - Muhammad Umar

14

组件数组

产生此错误的常见方式是使用组件数组,并使用位置索引从该数组中选择要呈现的组件。我经常看到这样的代码:

const checkoutSteps = [Address, Shipment, Payment]

export const Checkout = ({step}) => {

  const ToRender = checkoutSteps[step]

  return (
    <ToRender />
  )
}

这不一定是坏的代码,但如果你使用错误的索引调用它(例如-13在这种情况下),ToRender组件将会是undefined,从而抛出React.createElement:type is invalid...错误:

<Checkout step={0} /> // <Address />
<Checkout step={1} /> // <Shipment />
<Checkout step={2} /> // <Payment />
<Checkout step={3} /> // undefined
<Checkout step={-1} /> // undefined

一种理性的解决方案

您应该使用更明确的方法,避免使用魔数并使用 PropTypes 来保护自己和同事,以防止这种难以调试的代码:

const checkoutSteps = {
  address: Address,
  shipment Shipment,
  payment: Payment
}

const propTypes = {
  step: PropTypes.oneOf(['address', 'shipment', 'payment']),
}

/* TIP: easier to maintain
const propTypes = {
  step: PropTypes.oneOf(Object.keys(checkoutSteps)),
}
*/

const Checkout = ({step}) => {

  const ToRender = checkoutSteps[step]

  return (
    <ToRender />
  )
}

Checkout.propTypes = propTypes

export default Checkout

您的代码将如下所示:

// OK
<Checkout step="address" /> // <Address />
<Checkout step="shipment" /> // <Shipment />
<Checkout step="payment" /> // <Payment />

// Errors
<Checkout step="wrongstep" /> // explicit error "step must be one of..."
<Checkout step={3} /> // explicit error (same as above)
<Checkout step={myWrongVar} /> // explicit error (same as above)

这种方法的好处

  • 代码更加明确,您可以清楚地看到要呈现的内容
  • 您无需记住数字及其隐藏的含义(1 代表地址,2 是...)
  • 错误也更加明显
  • 不会给您的同事带来麻烦 :)

11
你需要了解命名导出默认导出。请参阅When should I use curly braces for ES6 import?
在我的情况下,我通过从以下更改来解决它:
import Provider from 'react-redux'
import { Provider } from 'react-redux'

我已经将我的问题缩小到这个提供程序。然而,我已经在使用 { Provider } 符号。 - Jason G

5

当我将CSS文件添加到与组件文件相同的文件夹中时,出现了这个问题。

我的导入语句是:

import MyComponent from '../MyComponent'

当只有一个文件MyComponent.jsx时,这是可行的。(我在一个例子中看到了这种格式并尝试了一下,然后就忘记了我这样做了)

当我将MyComponent.scss添加到同一个文件夹中时,导入失败了。也许JavaScript加载了.scss文件,因此没有错误。

我的结论:即使只有一个文件,请始终指定文件扩展名,以防稍后添加另一个文件。


大家好,我是新手。我认为对我来说问题肯定是命名约定,对我来说上面的逻辑听起来是正确的。 - tony2tones

5
循环依赖也是其中一个原因。[一般情况下]

1
是的,那也是我的情况。 - Or Assayag

4
在我的情况下,VS Code让我失望了。
以下是我的组件层次结构:
<HomeScreen> =>  <ProductItemComponent> =>  <BadgeProductComponent>

我导入了错误的ProductItemComponent。实际情况是,该组件以前在shared文件夹中,但后来被移动到home文件夹中。但是当我将该文件移动到另一个文件夹时,导入没有更新,仍然保持不变:

../shared/components

同时,组件工作正常且VS Code未显示错误。但是当我将新的BadgeProductComponent添加到ProductItemComponent中时,出现了一个渲染错误,我认为问题出在新的BadgeProductComponent上,因为当该组件被移除时,一切正常!
甚至更多的是,如果我通过快捷键进入具有../shared/components地址的ProductItemComponent,那么VS Code会将我重定向到带有../home/components地址的Home文件夹。
总的来说,请检查所有组件级别的导入正确性

3

我没有使用 React Fragments


function Bar({ children }) {

  return (
    <div>
     {children}
    </div>
  );
}

function Foo() {
  return (
    <Bar>
      <Baz/>
      <Qux/>
    </Bar>
  );
}

上面的代码会抛出上面的错误。但是以下代码可以解决这个问题:
<Bar>
  <>
    <Baz/>
    <Qux/>
  </>
</Bar>

当我添加ReactCollapsingTable (yarn add react-collapsing-table)时,发生了这种情况。我设法在编译的react-collapsing-table模块中在此警告(导致水合错误)和检测到的'window is not defined'之间进行交替... 即使使用<div>...</div>包装也无济于事。 - superk
最终我重新从头开始重构整个项目(而不是热加载),问题再次出现。在这里找到了解决方案:https://dev59.com/IlcP5IYBdhLWcg3wtML_#43969990(使用```require(module-name).default```代替```import module from 'module-name'```) - superk

3
我认为在解决这个错误时,最重要的是要意识到当您尝试实例化不存在的组件时,它就会出现。 这个组件不一定需要被导入。 在我的情况下,我是将组件作为属性传递的。 在一些重构后,我忘记更新其中一个调用,以正确地传递组件。 不幸的是,由于JS没有静态类型,我的错误没有被捕获,花费了一些时间才找到问题所在。
要解决这个错误,请在渲染组件之前检查组件,确保它是您预期的组件类型。

“当您尝试实例化一个不存在的组件时,它会显现出来”——这就是我的情况。该组件已经被正确地导入/导出,但我通过 react-router 中的授权路由将其作为 prop 传递时忘记将 render={props => <Component {...props} />} 更改为 component={Component} - displacedtexan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接