babel-loader, webpack, ES2015 modules: "Element type is invalid"

- 1 answer

Ad

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.

Ad

Answer

Ad

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.

Ad
source: stackoverflow.com
Ad