8

Building a ReactJS application with Webpack. Recently interested in using code splitting to reduce app size.

I've tried implementing a custom HOC that wrapped System.import():

/* async/index.tsx */
... at a very high level looked like...
class Async extends React ... {
    componentWillMount() {
        this.props.load.then(c => {
            this.component = c;
            this.setState({loaded:true});
        }
    }
    render() {
        return this.component ? <this.component.default {...this.props} /> : <span />;
    }
}

/* async/foo/index.tsx */
import Async from 'async';
const Foo = (props) => (
    <Async
      {...props}
      load={System.import('async/foo/body')}
    />);

class Foo ... {
    ...
    render = () => <Foo myProp={'bar'} />;
}

Currently I'm trying react-loadable (https://github.com/thejameskyle/react-loadable), a package that does essentially the same thing but with some extra bells n whistles.

Problem

Both methods work fine locally, but do not work when deployed. Webpack configurations are derived from React-Starter-App, from early March 2017. My gut tells me that the webpack config is the source of the problem, but I'm not sure how to debug this.

Dev Config (works)

/* relevant configs */
module.exports = {
  entry: [
      require.resolve('react-dev-utils/webpackHotDevClient'),
      require.resolve('./polyfills'),
      paths.appIndexJs
  ],
  output: {
    path: paths.appBuild,
    pathinfo: true,
    filename: 'static/js/bundle.js',
    publicPath: publicPath
  },
  ...
  plugins: [
    new ExtractTextPlugin({filename: 'style.css', allChunks: true}),
    new InterpolateHtmlPlugin(env.raw),
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
    }),
    new BundleAnalyzerPlugin(),
    new webpack.DefinePlugin(env.stringified),
    new webpack.HotModuleReplacementPlugin(),
    new CaseSensitivePathsPlugin(),
    new webpack.LoaderOptionsPlugin({
      debug: true
    }),
    new WatchMissingNodeModulesPlugin(paths.appNodeModules)
  ]
}

Staging Config (not working)

module.exports = {
  bail: true,
  devtool: 'source-map',
  entry: [
    require.resolve('./polyfills'),
    paths.appIndexJs
  ],
  output: {
    path: paths.appBuildStaging,
    filename: 'static/js/[name].[chunkhash:8].js',
    chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
    publicPath: publicPath,
  },
  resolve: {
    modules: ['node_modules', paths.appNodeModules].concat(paths.nodePaths).concat(paths.appSrc),
    extensions: ['.ts', '.tsx', '.scss', '.js', '.json', '.jsx']
  },
  ...
  plugins: [
    new InterpolateHtmlPlugin(env.raw),
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module) {
        // this assumes your vendor imports exist in the node_modules directory
        return module.context && module.context.indexOf('node_modules') !== -1;
      }
    }),
    //CommonChunksPlugin will now extract all the common modules from vendor and main bundles
    new webpack.optimize.CommonsChunkPlugin({ 
      name: 'manifest' //But since there are no more common modules between them we end up with just the runtime code included in the manifest file
    }),
    new BundleAnalyzerPlugin(),
    new webpack.DefinePlugin(env.stringified),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        screw_ie8: true, //. React doesn't support IE8
        warnings: false,
        drop_console: false,
      },
      mangle: {
        screw_ie8: true,
      },
      output: {
        comments: false,
        screw_ie8: true,
      },
      sourceMap: true,
    }),
    new ExtractTextPlugin({
      filename: cssFilename,
    }),
    new ManifestPlugin({
      fileName: 'asset-manifest.json',
    }),
  ]
}

Errors: react-loadable swallows all errors (palm + face) so I'm not able to provide useful errors from that code. My custom component would throw an error in the bootstrap loader bootstrap 6a12c6c…:54 Uncaught (in promise) TypeError: Cannot read property 'call' of undefined

In the network traffic for my custom HOC, the extra bundle is not loaded. In the network traffic for react-loadable, the bundle is loaded, but it is never processed.

6
  • Oh hi there! (Yes, you, dear reader) For new devs coming this question, can anyone let me know why I haven't received any comments/answers/questions here? Commented Jun 6, 2017 at 23:41
  • too lazy to read Commented Jun 23, 2017 at 12:19
  • but i have similar issue Commented Jun 23, 2017 at 12:19
  • @TarasLukavyi upvote the question, maybe we can find someone to help us Commented Jun 27, 2017 at 17:35
  • 1
    I've heard there are issues with webpack and chunking when you minify the code. Supposedly it decides on how to chunk BEFORE it is minified, so the chunking can be erratic. Would be worth looking into... Commented Oct 17, 2017 at 1:11

1 Answer 1

1

So after all this time, after upgrading Typescript and Webpack, it turns out that using the CommonsChunk plugin was screwing it up somehow.

Have not yet investigated why, but commenting out the following worked:

// new webpack.optimize.CommonsChunkPlugin({
//   name: 'vendor',
//   minChunks: function (module) {
//     return module.context && module.context.indexOf('node_modules') !== -1;
//   }
// }),
1
  • thanks @robbymurphy for the tip, I'm pretty sure you helped me debug this Commented Feb 19, 2018 at 6:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.