1
votes

I can't seem to get webpack's externals configuration for React correctly

The app works when react is imported as a vendor bundled from node_modules. But when I remove the vendor bundle and try to use react from a CDN I get the following error.

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. printWarning @ react.js:3640 warning @ react.js:3664 createElement @ react.js:2357 module.exports @ app.js:122 webpack_require @ app.js:20 (anonymous) @ app.js:429 webpack_require @ app.js:20 module.exports @ app.js:66 (anonymous) @ app.js:69

Full Code at https://github.com/ericnoguchi/react-testing

Here is my webpack config

let LiveReloadPlugin = require('webpack-livereload-plugin');
let ExtractTextPlugin = require("extract-text-webpack-plugin");
let webpack = require('webpack');

module.exports = {
    entry: {
        'app': './@Client.js',
        // 'vendor': [
        //     "react",
        //     "react-dom",
        //     "react-router"
        // ],
    },
    output: {
        path: __dirname + '/_dist',
        publicPath: '/',
        filename: "[name].js"
    },
    externals: {
        "react": 'React',
        "react-dom": 'ReactDOM',
        "react-router": 'ReactRouter'
    },
    devServer: {
        contentBase: '_dist'
    },
    module: {
        loaders: [
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'sass-loader']
                })
            },
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
                query: {
                    presets: [
                        "react",
                        "es2015",
                        "stage-2"
                    ]
                }
            }
        ]
    },
    plugins: [
        new webpack.DefinePlugin({
            "process.env": {
                BROWSER: JSON.stringify(true)
            }
        }),
        // new webpack.optimize.CommonsChunkPlugin({
        //     name: 'vendor',
        //     filename: 'vendor.js',
        //     minChunks: Infinity
        // }),
        new ExtractTextPlugin("css/[name].css"),
        new LiveReloadPlugin()
    ]
};

And there is the layout component

import React, { Component } from 'react';
import { Link } from 'react-router'

// http://stackoverflow.com/questions/30347722/importing-css-files-in-isomorphic-react-components
if (process.env.BROWSER) {
  require('./layout.scss');
  console.log('lalala')
}

export class Layout extends Component {
  handleClick() {
    alert(0);
  }
  render() {
    let {custom, children} = this.props;
    return (
      <html>
        <head>
          <title>{custom.title}</title>
          <link rel="stylesheet" type="text/css" href="css/app.css" />
        </head>
        <body>
          <h1>{custom.title}</h1>
          <button onClick={this.handleClick}>Click me</button>
          {children}
          <ul>
            <li>
              <Link to="/">Index</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/foo">404</Link>
            </li>
          </ul>


          <script dangerouslySetInnerHTML={{
            __html: 'window.PROPS=' + JSON.stringify(custom)
          }}></script>
          {/*<script src="vendor.js"></script>*/}
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js" />
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js" />
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/3.0.2/react-router.js" />
          <script src="app.js" />
        </body>
      </html>
    );
  }
}

Client APP

import ReactDOM from 'react-dom';
import routes from './routes/routes.jsx'

ReactDOM.render(routes, document);

Error with source maps

1
Not related to your question, but how you asked it; this is relevant reading for you. - Dan
Where is your ReactDOM.render call? (keep all code relevant in the question). Can you enable source maps so you can better see where the actual error is (and show us? app.js:line does not help given that it's a build artifact) - Dan
@Dan Pantry I enabled webpacks dev tool source maps but it does not appear to be of any help see image - ericsicons

1 Answers

0
votes

React is working just fine judging by the error you're pasting. Your problem is somewhere else.

There are however multiple flaws in your code. First of all it's not very common to generate the full page from React, and I see your html, headand body tags inside your render method.

Second, if you're loading the React CDN scripts from React, how is that supposed to work? :) It's a miracle you're getting that error at all.

Have an index.html with the basic HTML skeleton. If you're doing server-side rendering, have a simple template do the same and embed your server-side rendering of the React in a <div id="app"></div> placeholder.