React server-side rendering with hot module reload - reactjs

I've been doing a lot of reading on this, it seems that a lot of boilerplates available on github that enable both of these use webpack-dev-server to rebuild the client bundle. However, I'm not sure how or if the server is hot-module reloaded. Is there a way to configure webpack to enable HMR on the server-side? Otherwise, it seems that anything I change won't be rendered by the server, only refreshed on the client.
My webpack config looks something like this:
module.exports = {
entry: "./js/client.js",
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
}
}
]
},
output: {
path: __dirname + "/public/",
filename: "client.min.js"
}
};
server.js:
app.get('/', function(request, response) {
var html = ReactDOMServer.renderToString(React.createElement(Component));
response.send(html);
});
Component.js:
module.exports = React.createClass({
_handleClick: function() {
alert();
},
render: function() {
return (
<html>
<head>
<title>{this.props.title}</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<h1>{this.props.title}</h1>
<p>isn't server-side rendering remarkable?</p>
<button onClick={this._handleClick}>Click me</button>
<script dangerouslySetInnerHTML={{
__html: 'window.PROPS=' + JSON.stringify(this.props)
}} />
<script src="/client.min.js" />
</body>
</html>
);
}
})
and client.js:
var props = window.PROPS;
ReactDOM.render(React.createElement(Component, props), document);
I use webpack-dev-server --content-base src --inline --hot to run webpack

You're going to want to run your code via your server.js. Create a script in your package.json to run that file.
For example:
"serve":"nodemon server.js"
Then, add an alternative to webpack-dev-server which is the webpack-dev-middleware and webpack-hot-middlware which can help you with HMR.
This is how you're server.js will look like:
import express from 'express';
import webpack from 'webpack';
import path from 'path';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
const port = 5000;
const app = express();
const config = require(path.resolve("./webpack.config.js"));
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: config.output.publicPath,
}));
app.use(webpackHotMiddleware(compiler));
app.get("*", function(req, res, next) {
res.sendFile( path.join( __dirname, '/path/to/your/index.html') );
});
app.listen(port, function(err) {
if(err) {
return console.log(err);
}
return console.log(`App is now running on http://localhost:${port}`);
});
Then, run your script, like so:
npm run serve
And just visit the port on the browser, your client will then show up.
Hope this helps.

Related

Use Pure Bootstrap v4 on ReactJs is NOT working

I was desperately want to use pure Bootstrap v4 on my React.js app. I created my app using create-react-app. So, i put Bootstrap assets on index.html (in public folder).
At the first try, it was working well. Then, i added some dependencies like react-router-dom, react-router-config, and prop-types. Suddenly, it displayed almost a blank page.
1) Project Folder
2) kodebaru/webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, '/client/src/index.jsx'),
output: {
path: path.join(__dirname, '/client/dist/js'),
filename: 'app.js',
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: path.join(__dirname, '/client/src'),
loader: 'babel-loader',
query: {
presets: ["react", "es2015"]
}
}
]
},
watch: true
};
3) kodebaru/server.js
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.resolve(__dirname, './backend/static/')));
app.use(express.static(path.resolve(__dirname, './client/dist/')));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, './backend/static/', 'index.html'));
});
const PORT = process.env.PORT || 3500;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}!`);
});
4) kodebaru/backend/static/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Kodebaru</title>
<link rel="stylesheet" href="./bootstrap/bootstrap.min.css">
</head>
<body>
<div id="root"></div>
<script src="/js/app.js"></script>
</body>
</html>
5) result
6) Warning Message
I knew there is a framework bootstrap for react app (reactstrap). But, this time i want to know how to use pure bootstrap v4 in react app ?
It is too late. But, i think it is good to answer my own question since there is no one. The solution was to install bootstrap using npm, import bootstrap on index.jsx, add jQuery, Popper.js, and css loader on webpack configuration file.
a) Install bootstrap using npm
npm install bootstrap#4.0.0-beta
b) Import bootstrap on index.jsx
import 'bootstrap';
require('bootstrap/dist/css/bootstrap.css');
c) Add jQuery and Popper.js on webpack
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
Popper: ['popper.js', 'default']
})
d) Add css loader on webpack
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
The full implementation is below :
1) index.jsx
import React from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from "redux";
import promise from "redux-promise";
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import routes from './routes.js';
import reducers from './redux/reducers/index.js'
import 'bootstrap';
require('bootstrap/dist/css/bootstrap.css');
const createStoreWithMiddleware = createStore(reducers, {}, compose(applyMiddleware(promise)));
ReactDOM.render(
<Provider store = {createStoreWithMiddleware}>
<Router>
{/* kick it all off with the root route */}
{renderRoutes(routes)}
</Router>
</Provider>,
document.getElementById('root')
);
if(module.hot) {
module.hot.accept('./routes.js', function() {
routes();
})
}
2) webpack.config.js
const webpack = require('webpack');
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './app/index.js',
output: {
filename: 'index_bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.join(__dirname, '/app'),
exclude: '/node_modules',
loader: 'babel-loader',
query: {
presets: ["react", "es2015"],
plugins: [
"transform-react-pug",
"transform-react-jsx",
"transform-object-rest-spread"
]
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
Popper: ['popper.js', 'default']
}),
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './index.html'
})
],
devtool: 'cheap-module-eval-source-map',
watch: true
};

webpack express Uncaught SyntaxError: Unexpected token <

i need help, with this error "webpack express Uncaught SyntaxError: Unexpected token <" can u tell me what i doing wrong ? webpack and express server not reported eny erorrs..
file struture:
app
package.json
server.js
webpack.config.js
-public
--index.html
--bundle.js
-node_modules
-src
--config.js
--index.js
--about
---about.html
--todos
---todos.html
webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
devtool: 'inline-source-map',
entry: ['./src'],
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
resolve: {
modulesDirectories: ['node_modules', 'src'],
extension: ['', '.js']
},
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015']
}},
{ test: /\.html$/,
loader: 'raw'}
]
},
devServer: {
hot: true,
proxy: {
'*': 'http://localhost:3000'
}
}
};
index.js
import angular from 'angular';
import appModule from 'config';
angular.bootstrap(document, [appModule.name]);
index.html
<html>
<head>
<title>MEAN ToDo App</title>
<base href="/">
</head>
<body>
<div ui-view></div>
<script src="bundle.js"></script>
</body>
</html>
config.js
import angular from 'angular';
import uiRouter from 'angular-ui-router';
const app = angular.module('app', [uiRouter]);
app.config(($stateProvider, $urlRouterProvider, $locationProvider) => {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('todos', {
url: '/',
template: require('todos/todos.html'),
})
.state('about', {
url: '/about',
template: require('about/about.html')
});
$locationProvider.html5Mode(true);
});
export default app;
server.js
var express = require('express');
var app = express();
var path = require('path');
var PORT = process.env.PORT || 3000;
app.all('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.listen(PORT, function() {
console.log('Server running on ' + PORT);
});
The problem is in the node code:
app.all('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
All routes are returning index.html. Try removing the *.
Your express server is misconfigured – it always returns index.html.
Try this instead:
app.use(express.static('/'));
app.listen(3000, function() {
console.log('listening');
});
Or use http-server from npm.

Express + Webpack error after page reload

I have deployed a React app to Heroku. It works fine and all, but whenever I refresh a page that it's not the index ('/'), I keep getting an error: Cannot GET /project/post1.
I have no idea why this is happenning. I configured my express server to serve my static files on all routes, but its not working.
My file structure looks something like this:
-dist
- bundle.js
- index.html
-server.js
This is my server.js file:
var express = require('express');
var path = require('path');
var app = express();
var isProduction = process.env.NODE_ENV === 'production';
var port = isProduction ? process.env.PORT : 3000;
var publicPath = path.resolve(__dirname, './dist');
app.use(express.static(publicPath));
app.get('*', function(request, response) {
response.sendFile(path.join(publicPath))
});
// And run the server
app.listen(port, function () {
console.log('Server running on port ' + port);
});
This is my webpack.prod.config file:
var webpack = require('webpack');
module.exports = {
entry: {
main: './src/index.js'
},
resolve: {
extensions: ['', '.js', '.jsx']
},
output: {
path: __dirname + '/dist',
publicPath: '/dist/',
filename: 'bundle.js'
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'react-hot!babel'
},
{
test: /\.scss$/,
loader: 'style!css!sass'
}]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
})
]
};
I call my bundle.js file in the index.html like this:
<body>
<div id="app"></div>
<script src="/bundle.js"></script>
</body>
Your routing happens on the client, not the server. Express doesn't know what it should do on a GET request on /project/post1.
So we send all requests to the client, where you have react-router running. We can do that by configuring a fallback in the server.js file, for example like this:
import fallback from 'express-history-api-fallback';
import express from 'express';
import http from 'http';
// Set up express
const app = express();
const server = http.createServer(app);
const root = `${__dirname}`;
// History Fallback for express
app.use(express.static(root));
app.use(fallback('index.html', { root }));
// Listen
server.listen(8080, '0.0.0.0');

ReactDOM does not render component into html

Just started redux, get stuck on a weird situation, webpack shows no error, but in the html, the component did not get rendered. The file structure:
dist
bundle.js
node_modules
src
index.js
.babelrc
index.html
package.json
server.js
webpack.config.js
index.html
<html>
<head>
<title>jie blog</title>
<meta charset="utf-8">
</head>
<body>
<div id="root"></div>
</body>
<script src="dist/bundle.js">
</script>
</html>
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { Router, Route, Redirect, browserHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
class App extends React.Component{
render(){
return(
<div>
hello world
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
server.js
var http = require('http')
var express = require('express')
var httpProxy = require('http-proxy')
var fs = require('fs')
var babelrc = fs.readFileSync('./.babelrc')
var config = JSON.parse(babelrc)
require('babel-core/register')(config)
var proxy = httpProxy.createProxyServer({})
var app = express()
app.use(require('morgan')('short'))
// webpack
var webpack = require('webpack')
var config = require('./webpack.config')
var compiler = webpack(config)
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}))
app.use(require('webpack-hot-middleware')(compiler))
app.all(/^\/api\/(.*)/, function api(req, res){
proxy.web(req, res, {
target: 'http://localhost:5000'
})
})
app.get(/.*/, function root(req, res){
res.sendFile(__dirname + '/index.html')
})
const server = http.createServer(app)
server.listen(process.env.PORT || 3000, function(){
const address = server.address()
console.log('listening on %j', address)
})
webpack.config.js
const webpack = require('webpack')
const path = require('path')
module.exports = {
devtool: 'source-map',
entry: [
'webpack-hot-middleware/client',
path.resolve(__dirname, 'src/index.js')
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
resolve: {
extensions: ['', '.jsx', '.js', '.json', '.scss']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
}
}
output html
<body>
<div id="root"></div>
<script src="dist/bundle.js">
</script>
</body>
It looks it's not an issue with ReactDOM but instead a configuration problem.
In server.js
...
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath // <--- /static/
}))
...
publicPath specifies the public URL address of your bundle.js when referenced in a browser. So /static/bundle.js as you set in your webpack.config.js. Ok fine !
So Index.html need to request for /static/bundle.js. and NOT dist/bundle.js
...
<script src="static/bundle.js"></script>
...
Check output.publicPath configurations for webpack for more informations
As index.html requested for your bundle at /dist and webpack-dev-middleware handle requests at /static, an index.html file was returned instead of a your bundle.js
app.get(/.*/, function root(req, res){
res.sendFile(__dirname + '/index.html')
})
It's why you got Uncaught SyntaxError: Unexpected token < bundler.js:1.
The problem is that Babel is transpiling only .jsx files:
test: /\.jsx?$/,
while your file with JSX has a .js extension (index.js)

Can't make react-hot-loader and webpack-dev-server work with react-router

I'm trying to use react-hot-loader with webpack-dev-server and react-router, but when I try to access localhost:3000/ I get :
Cannot GET /
Of course, it works when I try to access localhost:8000/. I tried to follow react-hot-boilerplate, without success.
Here's my code:
server.js
const http = require('http');
const express = require('express');
const consolidate = require('consolidate');
const bodyParser = require('body-parser');
const routes = require('./routes');
const app = express();
app.set('views', 'public/pages'); // Set the folder-name from where you serve the html page.
app.set('view engine', 'html');
app.engine('html', consolidate.handlebars);
app.use(express.static('public')); // Set the folder from where you serve all static files
app.use(express.static('public/dist')); // Set the folder from where you serve all static files
app.use(bodyParser.urlencoded({ extended: true }));
const portNumber = 8000;
http.createServer(app).listen(portNumber, () => {
console.log(`Server listening at port ${portNumber}`);
routes.initialize(app);
});
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
colors: true,
historyApiFallback: true,
inline: true,
hot: true,
}).listen(3000, 'localhost', (err) => {
if (err) {
console.log(err);
}
});
routes.js (so all the routes point to the router)
function initialize(app) {
const routes = [
'/',
'/login',
];
routes.forEach((route) => {
app.get(route, (req, res) => {
res.render('main-content.html');
});
});
}
exports.initialize = initialize;
webpack.config.js
const webpack = require('webpack');
const path = require('path');
const nodeDir = `${__dirname}/node_modules`;
const config = {
resolve: {
alias: {
react: `${nodeDir}/react`,
'react-dom': `${nodeDir}/react-dom`,
'react-router': `${nodeDir}/react-router`,
'react-bootstrap': `${nodeDir}/react-bootstrap`,
velocity: `${nodeDir}/velocity-animate`,
moment: `${nodeDir}/moment`,
slimscroll: `${nodeDir}/slimscroll`,
},
},
entry: {
routes: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./public/src/routes/js/main',
],
vendors: [
'react', 'react-dom', 'react-router', 'react-bootstrap',
'velocity', 'moment', 'slimscroll',
],
},
output: {
path: path.join(__dirname, 'public/dist'),
publicPath: path.join(__dirname, 'public/dist'),
filename: 'bundles/[name].bundle.js',
chunkFilename: 'chunks/[name].chunk.js',
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: path.join(__dirname, 'public'),
loader: 'react-hot',
},
{
test: /\.js$/,
include: path.resolve(__dirname, 'public'),
loader: 'babel',
},
{
test: /\.css$/,
include: path.join(__dirname, 'public'),
loader: 'style!css-loader?modules&importLoaders=1' +
'&localIdentName=[name]__[local]___[hash:base64:5]',
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.CommonsChunkPlugin('vendors', './bundles/vendors.js', Infinity),
],
};
module.exports = config;
scripts
"scripts": {
"dev": "webpack --config webpack.config.js",
"hot": "webpack-dev-server --devtool eval --progress --colors --inline --hot",
"build": "webpack -p --config webpack.config.prod.js"
}
main-content.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Brigad Admin Panel</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- Customs -->
<link rel="stylesheet" href="styles/global.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,600,400italic,700,600italic,700italic,800,800italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
</head>
<body class="hold-transition">
<div id="content"></div>
<!--<script src="dist/bundles/vendors.js"></script>-->
<!--<script src="dist/bundles/routes.bundle.js"></script>-->
<script src="http://localhost:8080/public/dist/bundles/vendors.js"></script>
<script src="http://localhost:8080/public/dist/bundles/routes.bundle.js"></script>
</body>
</html>
entry point
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import RootRoute from './components/RootRoute';
render(
<Router history={browserHistory} routes={RootRoute} />,
document.getElementById('content')
);
How can I make react-hot-loader to work?
Thanks in advance.
You should provide localhost:3000 for publicPath in webpack.config.
In the dev-server config you maybe need to add contentBase option pointing to your build output (./public/dist).
Take a look at this https://github.com/webpack/docs/wiki/webpack-dev-server#content-base

Resources