babel-loader, webpack, ES2015 modules: "Element type is invalid"
Github repo with everything in: https://github.com/b-paul/react-lifecycle
Update 12/18: A large part of the problem was the npm commands used to run the project. I had noticed that npm build
was not successful, but npm start
reported building OK. Full answer below on why that didn't work as expected. The rest of this question is being kept for posterity.
I'm having trouble with basic setup for my first webpack project. I'm using React and Babel, and the following webpack.config.js
:
var path = require('path');
module.exports = {
entry: [ path.resolve('./js/app.js'),
'webpack-dev-server/client?http://localhost:8080' ],
output: {
path: path.resolve('./js/build'),
filename: 'app.min.js'
},
module: {
loaders: [
{ test: /\.jsx?$/,
loader: 'babel',
query: {
presets: ['react', 'stage-1', 'es2015']
},
include: path.resolve('js/') } ]
},
devtool: 'source-map'
};
js/app.js
import Lifecycle from './components/lifecycle';
import React from 'react';
import {render} from 'react-dom';
var shell = document.createElement('div');
shell.className = 'app-shell';
document.body.appendChild(shell);
render(<Lifecycle />, shell);
js/components/lifecycle.js
import React from 'react';
class Lifecycle extends React.Component {
render() {
return (
<div>Hello, world</div>
);
}
}
export default Lifecycle;
The above builds without errors, but it won't render "Hello, world". I'm getting the error, "Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined." in the browser console.
I can trace this as far as some generated babel code that tries to check if my module Lifecycle
is an ES6 module (babel recognizes it as such) and expects it to have a property default
on the internal module object (it doesn't). Here is that generated code:
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _lifecycle = __webpack_require__(1);
var _lifecycle2 = _interopRequireDefault(_lifecycle);
var _react = __webpack_require__(2);
var _react2 = _interopRequireDefault(_react);
var _reactDom = __webpack_require__(159);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(0, _reactDom.render)(_react2.default.createElement(_lifecycle2.default, null), document.body);
/***/ }
Final note: I referenced https://github.com/code0wl/react-example-es2015 several times for setup, and I can clone and build that example repo into a working app with no problems. I'm realize that I must've missed some essential part of what's happening in that repo, but I can't see it.
Answer
This question should have been called "why won't my NPM scripts update my bundle."
At some point early in development, I had successfully bundled my code with webpack. That bundle was on disk as js/build/app.min.js
. After that point, I thought I was using NPM scripts from package.json
to rebuild the app with each change, but in-browser, I was still getting the old behavior (from the original bundle). I missed two facts:
Fact 1: npm build
is a recognized NPM command, but it does not invoke scripts.build
from package.json
. As in @zhongjie-wu 's answer, I needed to do npm run build
. npm start
, on the other hand, does invoke scripts.start
, which successfully built a bundle with webpack-dev-server
.
Fact 2: webpack-dev-server
does not recognize output.path
from the webpack config file as part of the route when serving the rebuilt bundle. Because of this, given the configuration I used, webpack
builds to /js/build/app.min.js
, but webpack-dev-server
serves that bundle from memory as /app.min.js
. Since my HTML references the original, I didn't see the dev server bundle.