Split component wasn't rendered after getComponent() - reactjs

I used Code Splitting for React Router to load component async, and I made it successfully. But when i opened my page in browser, i got nothing. Here're some key code snippets:
routeConfig
// When i removed the annotation below, it did render !
// import Home from './containers/Home/Home'
const routeConfig = [
{
path: '/',
component: App,
//indexRoute: {
// component: Home
//}
getIndexRoute(location, callback) {
require.ensure([], (require) => {
callback(null, require('./containers/Home/Home'))
}, 'Home');
}
}];
Home.jsx
console.log('HOME'); // It works!
class Home extends Component {
render() {
console.log('HOME COMPONENT'); // not working!
}
}
Any ideas? I'm stuck here :(
Here're my full webpack config codes:
/**
* webpack config
*/
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: {
vendor: [
'react',
'react-dom',
'redux',
'react-redux',
'react-router',
'redux-logger',
'redux-thunk'
],
main: [
'./html/index'
]
},
output: {
// path: path.join(__dirname, 'html/dist'),
path: './html/dist',
publicPath: '/html/dist/',
filename: '[name].js',
chunkFilename: '[name].js'
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.js|jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel', // 'babel-loader' is also a legal name to reference
query: {
plugins: ['transform-object-rest-spread'],
presets: ['react', 'es2015']
}
},
{
test: /\.less$/,
exclude: /(node_modules|bower_components)/,
loader: "style!css!less"
}
]
},
externals: {
//don't bundle the 'react' npm package with our bundle.js
//but get it from a global 'React' variable
// 'react': 'React',
// 'react-dom': 'ReactDOM',
// 'redux': 'Redux',
// 'react-redux': 'ReactRedux',
// 'react-router': 'ReactRouter',
// 'redux-logger': 'reduxLogger',
// 'redux-thunk': 'ReduxThunk'
},
// add this handful of plugins that optimize the build
// when we're in production
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: 'vendor'
})
]
};
Hope this would help.
Home.js did being loaded, but the Home component seems to be uninitialized.

I just figured it out. Refer to links below:
invariant-violation-the-root-route-must-render-a-single-element-error-in-react
react-router/issues/2588
react-router/issues/2881

Related

How to display console.logs using Webpack build?

I'm using Webpack to build a React app. I'm trying to use console.log to debug but they don't show up in the browser console or in the terminal. It doesn't seem to like there's too much on this (The only other question I've found on this is here: Webpack console.log output?)
webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/App.tsx',
mode: 'development',
module: {
rules: [
{
test: /\.(|ts|tsx|js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-env',
'#babel/preset-react',
'#babel/preset-typescript',
],
plugins: [
'#babel/plugin-proposal-class-properties',
],
},
},
},
{
test: /\.(css|scss)$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(jpe?g|gif|png|svg)$/i,
loader: 'url-loader',
options: {
limit: 25000,
esModule: false,
},
},
],
},
resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'] },
output: {
path: path.resolve(__dirname, 'dist/'),
publicPath: '/dist/',
filename: 'bundle.js',
},
devServer: {
contentBase: path.join(__dirname, 'public/'),
port: 3000,
publicPath: 'http://localhost:3000/dist/',
hot: true,
},
plugins: [new webpack.HotModuleReplacementPlugin()],
};
App.tsx
import React from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
console.log('test1') // this gets logged
render(<Hello />, document.querySelector('#main'));
Hello.tsx
import React, { RefObject } from 'react';
interface Props { }
interface State { }
console.log('test2') // this gets logged
export default class Hello extends React.Component<Props, State> {
private start() {
console.log('test3') // this doesn't get logged
}
constructor(p: Props) {
super(p);
this.start();
console.log('test4') // this gets logged
}
}
Any insights are appreciated!

Routes chunks are bundling external scripts in every chunk

In my webpack I've used externals which has React, React Dom, Redux etc.
Now when I implement my Route Chunking, every chunk which is generated re-bundles the external scripts again, so eventually my bundle size is very huge.
How can I avoid my individual chunks not to re-bundle the external scripts and use them from externals.
EDIT
Using https://chrisbateman.github.io/webpack-visualizer/ I can see that all my chunks are bundling common libs - which are are actually supposed to come from externals in webpack.
EDIT 2
webpack file
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: ['./src/containers/AppContainer', './src/index'],
devtool: 'cheap-module-source-map',
output: {
path: __dirname + '/dist',
publicPath: 'public/',
filename: 'bundle.js',
chunkFilename: '[name].[id].chunk.[chunkhash].js',
libraryTarget: 'umd'
},
target: 'web',
externals: {
antd: 'antd',
react: 'react',
'react-dom': 'react-dom',
'react-router': 'react-router',
redux: 'redux',
'react-redux': 'react-redux',
immutable: 'immutable',
},
resolve: {
modules: [
path.join(__dirname, '../node_modules')
],
extensions: ['.js', '.jsx', '.json'],
alias:{
constants: path.resolve(__dirname, './src/constants'),
actions: path.resolve(__dirname, './src/actions'),
styles: path.resolve(__dirname, './src/styles'),
utils: path.resolve(__dirname, './src/utils')
}
},
resolveLoader: {
modules: [
path.join(__dirname, '../node_modules')
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
comments: false
})
]
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// Ignore local .babelrc files
babelrc: false,
presets: [
['es2015', { modules: false }],
'react'
],
plugins: [
'react-html-attrs',
'transform-class-properties',
'transform-decorators-legacy',
'transform-object-rest-spread',
[
'import', {
libraryName: 'antd'
}
]
]
}
},
{ test: /\.png$/, loader: 'file-loader' },
{
test: /\.s?css$/i,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.s?less$/i,
exclude:'/node_modules/',
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader',
options: {
limit: 100000
}
},
{
test: /\.eot\?iefix$/,
loader: 'url-loader',
options: {
limit: 100000
}
},
{
enforce: 'pre',
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/,
options: {
configFile: './eslint/.eslintrc',
failOnWarning: false,
failOnError: false
}
}
]
}
};
Routes file
import React from 'react';
import { Route, IndexRoute } from 'react-router';
export default (
<Route path='/base/'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null, require('./containers/AppContainer').default);
});
} }>
<Route path='/route1'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./containter1')
.default);
});
} }
/>
<Route path='/route2'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./container2')
.default);
});
} }
/>
<Route path='/route3'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./container3')
.default);
});
} }
/>
</Route>
);
Try to change your externals section like this:
externals: {
React: require.resolve('react'),
'window.React': require.resolve('react'),
ReactDOM: require.resolve('react-dom'),
'window.ReactDOM': require.resolve('react-dom')
}
Also, remove import React from 'react' from your code. Just use React.
EDIT
Apologies, I edited my answer. Just realised my mistake. I changed the code. The key inside your externals will be the name of the global variable. Usually it's also safer to put it inside the window object

Can not export webpack.config when modules:false

Inside my index.js I am using webpack-dev-middleware/webpack-hot-middleware where I require my webpack.config and use it for compiler.
index.js
const webpack = require('webpack')
const webpackConfig = require('../../webpack.config.js')
const compiler = webpack(webpackConfig)
const webpackDevMiddleware = require('webpack-dev-middleware')
app.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
hot: true,
noInfo: true,
stats: {
colors: true
}
}))
app.use(require('webpack-hot-middleware')(compiler))
I try to export my webpack.config using Common.js by require and module.exports but I get error
TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
webpack.config
'use strict'
const path = require('path')
const webpack = require('webpack')
const publicPath = path.resolve(__dirname, './src/client')
const buildPath = path.resolve(__dirname, './src')
process.noDeprecation = true
module.exports = {
devtool: 'source-maps',
performance: {
hints: false
},
context: publicPath,
entry: {
bundle: [
'react-hot-loader/patch',
'webpack-hot-middleware/client?reload=false&noInfo=true',
'script-loader!jquery/dist/jquery.min.js',
'script-loader!tether/dist/js/tether.min.js',
'script-loader!bootstrap/dist/js/bootstrap.min.js',
'./app.js'
]
},
output: {
path: path.join(buildPath, 'dist'),
filename: '[name].js',
publicPath: '/'
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
CountdownForm: path.resolve(__dirname, 'src/client/scenes/countdown/components/CountdownForm.jsx'),
Countdown: path.resolve(__dirname, 'src/client/scenes/countdown/index.jsx'),
..
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules|dist|build/,
loader: 'babel-loader',
options: {
plugins: [
[
'babel-plugin-react-css-modules',
{
context: publicPath,
filetypes: {
'.scss': 'postcss-scss'
}
}
]
]
}
},
{
test: /\.local\.(css|scss)$/,
use: [
'style-loader',
'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
'postcss-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, './src/client/styles/global/sass-resources.scss')
}
}
]
},
{
test: /^((?!\.local).)+\.(css|scss)$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery'
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
})
]
}
If I use ES6 (I am using babel and this usually works) using import statements at the top instead of require and export default instead of module.exports I get this error
Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration misses the property 'entry'.
All of this is because of modules:false inside my .babelrc If I remove that Common.js way works, but I need this. How can I export webpack.config to compiler using modules:false
{
"presets": [
"react",
["es2015", { "modules": false }],
"stage-0"
],
"plugins": [
"react-hot-loader/babel",
"transform-runtime"
]
}
transform-runtime adds import and that results in mixing import with your module.exports.
Simple fix would be to replace module.exports with es6 export
module.exports = { ...webpackconfig }
becomes
export default { ...webpackconfig }
and update your index.js to use default export
const webpackConfig = require('../../webpack.config.js').default
You can find more information about this on these issues
https://github.com/webpack/webpack/issues/4039
https://github.com/webpack/webpack/issues/3917

Webpack 2 configuration for Tree shaking and lazy loading with System.import on React project

I am new to webpack 2 and it's lazy loading, so far I have created project without lazy loading and code splitting, but now want to split my code into chunks and use System imports with React Router. I have created React Router part according to this article
this webpack 2 config file is below.
let webpack = require('webpack');
let path = require('path');
let ExtractTextPlugin = require('extract-text-webpack-plugin');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')),
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
}
});
let extractSCSS = new ExtractTextPlugin('[name].css')
module.exports = {
context: __dirname,
entry: {
bundle: './src/app/app-client.jsx',
styles: './src/app/sass/main.scss',
vendor: [
'react', 'react-dom'
]
},
output: {
filename: '[name].js',
chunkFilename: 'chunk.[id].[chunkhash:8].js',
path: './src/build',
},
resolve: {
extensions: ['.js', '.jsx']
},
devtool: 'cheap-module-source-map',
module : {
rules : [
{
test: /\.scss$/,
loader: extractSCSS.extract(['css-loader','sass-loader'])
},
{
test: /\.jsx?$/,
exclude: [/node_modules/, /libs/],
use: {
loader: "babel-loader",
query: {
presets: ['es2015', 'react', 'stage-2' ],
plugins: [ "transform-runtime" ]
}
}
},
{
test: /\.woff2?$|\.ttf$|\.eot$|\.svg$|\.png|\.jpe?g|\.gif$/,
use: {
loader:'file-loader'
}
}
]
},
plugins: [
extractSCSS,
devFlagPlugin,
new webpack.optimize.CommonsChunkPlugin({
name: 'bundle',
children: true,
async: true,
minChunks: 2
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
children: true,
async: true,
minChunks: 2
})
]
}
but webpack creates only two files, vendor and bundle, also size of bundle hasn't reduced after I separated React and React DOM.
this is my routes.
import App from './App.jsx';
function errorLoading(err) {
console.error('Dynamic page loading failed', err);
}
function loadRoute(cb) {
return (module) => cb(null, module.default);
}
export default {
component: App,
childRoutes: [
{
path: 'stock/info/:symbol(/:tab)',
getComponent(location, cb) {
System.import('./StockPage')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: '*',
getComponent(location, cb) {
System.import('./NoMatch')
.then(loadRoute(cb))
.catch(errorLoading);
}
}
]
};
my application runs, but lazy loading won't work of course, because I have no chunks of my modules within System.import.
Please help me to create right webpack config for performance of my application!
Thanks in advance and sorry if something is nonsense, since I am new to webpack.
Webpack2 switched from System.import() to import() to match the current proposed javascript feature. Which is in stage3 right now.
So you should be able to change your webpack config to include STAGE-3
{
test: /\.jsx?$/,
exclude: [/node_modules/, /libs/],
use: {
loader: "babel-loader",
query: {
presets: ['es2015', 'react', 'stage-2', 'stage-3' ],
plugins: [ "transform-runtime" ]
}
}
},
Or the dynamic-import plugin
{
test: /\.jsx?$/,
exclude: [/node_modules/, /libs/],
use: {
loader: "babel-loader",
query: {
presets: ['es2015', 'react', 'stage-2' ],
plugins: [ "transform-runtime", "syntax-dynamic-import"]
}
}
},
Then change your routes
export default {
component: App,
childRoutes: [
{
path: 'stock/info/:symbol(/:tab)',
getComponent(location, cb) {
import('./StockPage')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: '*',
getComponent(location, cb) {
import('./NoMatch')
.then(loadRoute(cb))
.catch(errorLoading);
}
}
]
};
See the webpack2 help page here for full docs on using import for code splitting and lazy loading.
https://webpack.js.org/guides/migrating/#code-splitting-with-es2015
https://github.com/tc39/proposal-dynamic-import
To enable Webpack2 tree shaking which only requires making one change to your babel setup.
presets: ['es2015', 'react', 'stage-2' ],
becomes
presets: [['es2015', { modules: false }], 'react', 'stage-2' ],
This is the article that I found out about treeshaking from: https://medium.freecodecamp.com/tree-shaking-es6-modules-in-webpack-2-1add6672f31b#.lv3ldgfhs

React, Webpack and Babel for Internet Explorer 9

Trying to support IE 9 for React. Upgraded to use babel 6.3.26 and babel-preset-es2015 and babel-preset-react for Webpack. However, when the file is loaded in IE 9, a syntax error occurs.
webpack.config.js
/* eslint-env node */
var path = require('path');
var packageJson = require('./package.json');
var _ = require('lodash');
var webpack = require('webpack');
var context = process.env.NODE_ENV || 'development';
var configFunctions = {
development: getDevConfig,
production: getProdConfig,
test: getTestConfig
};
var config = configFunctions[context]();
console.log('Building version %s in %s mode', packageJson.version, context);
module.exports = config;
function getLoaders() {
if (context.indexOf('test') === -1) {
return [
{
test: /\.js?$/,
exclude: /(test|node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015'],
plugins: ['transform-runtime']
}
}
]
} else {
return [
{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015'],
plugins: ['transform-runtime']
}
}
]
}
}
function getBaseConfig() {
return {
context: __dirname + "/src",
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
stats: {
colors: true,
reasons: true
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: _.union(
getLoaders(),
[
{
test: /\.scss$/,
loader: 'style!css!sass'
},
{
test: /\.eot$|\.svg$|\.woff$|\.ttf$/,
loader: 'url-loader?limit=30000&name=fonts/[name]-[hash:6].[ext]'
},
{
test: /\.(png|.jpe?g|gif)$/,
loader: 'url-loader?limit=5000&name=img/[name]-[hash:6].[ext]'
},
{
test: /\.mp4$/,
loader: 'url-loader?limit=5000&name=videos/[name]-[hash:6].[ext]'
}
]
)
}
};
}
function getDevConfig() {
return _.merge({}, getBaseConfig(), {
devtool: 'cheap-module-eval-source-map',
entry: [
'babel-polyfill',
'webpack-hot-middleware/client',
'./App'
],
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
eslint: {
emitError: false,
failOnError: false,
failOnWarning: false,
quiet: true
}
});
}
function getProdConfig() {
return _.merge({}, getBaseConfig(), {
devtool: 'source-map',
entry: [
'babel-polyfill',
'./App'
],
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
minimize: true,
compress: {
warnings: false
}
})
],
eslint: {
emitError: true,
failOnError: true
}
})
}
function getTestConfig() {
return _.merge({}, getBaseConfig(), {})
}
Checking bundle.js for the offending lines reveals the usage of const which is not ES5. Am I missing something here? Do I need to transpile ES6 code into ES5 for production usage?
IE9 is not compatible with ES6, so, yes, you must transform your ES6 code to ES5. I believe the problem is you aren't telling babel to use the react and es2015 presets. I'm sure you installed them on your machine, but the babel loader only does what you tell it.
Inside your getLoaders() function, add the presets to your babel loader configuration query:
query: {
plugins: ['transform-runtime'],
presets: ['react', 'es2015']
}
Hopefully, that works for you.
babel/babel-loader reference
I am using create-react-app (v16.4.2). I tried using the followings to get the default hello world working in IE9:
1:
import 'core-js/es6/map';
import 'core-js/es6/set';
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
2:
import "babel-polyfill";
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
But neither of them worked for me. I ended up adding the following line into my index.html file in the public folder and it fixed my issue:
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
More information is available at:https://polyfill.io/v2/docs/

Resources