1

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

ericsicons
  • 1,475
  • 3
  • 23
  • 38
  • Not related to your question, but how you asked it; this is relevant [reading for you](http://importblogkit.com/2015/07/does-not-work/). – Dan Feb 05 '17 at 05:08
  • 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 Feb 05 '17 at 05:09
  • @Dan Pantry I enabled webpacks dev tool source maps but it does not appear to be of any help see image – ericsicons Feb 05 '17 at 05:57
  • Did you check this https://webpack.js.org/guides/author-libraries/ ? – Oleg Pro Feb 05 '17 at 09:23

1 Answers1

-2

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.

CharlieBrown
  • 4,143
  • 23
  • 24
  • Yes I am using server side rendering so the react scripts should load as the server returns the full page rendered. What I don't understand is why it does not work with CDN but works if I use react from a Webpack vendor chunk which will be a script that goes over app.js. So its basically the same setup. – ericsicons Feb 05 '17 at 17:52
  • You're likely missing an `export default` in one of your components used in your `routes` then. – CharlieBrown Feb 06 '17 at 07:35