Routing issue in React with Webpack - reactjs

I routed in my app as the following:
render(
<Provider store={store}>
<Router history={browserHistory }>
<Route path={"/" component={TopContainer}>
<IndexRoute component={Login} />
<Route path='main' component={Maintainer} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
)
Then I deploy my production bundle.js and index.html directly under my web root (apache in ubuntu).
I can access my app at http://...com/
The problem is that after the main page is loaded with route
<Route path='main' component={Maintainer} />
the content in browser location becomes: http://...com/main
At this time, if I reload the page with the url (http://...com/main) I got a page not found error: "The requested URL /main was not found on this server."
Here the my webpack.production.config.js
var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
loaders.push({
test: /\.scss$/,
loader: ExtractTextPlugin.extract({fallback: 'style-loader', use : 'css-loader?sourceMap&localIdentName=[local]___[hash:base64:5]!sass-loader?outputStyle=expanded'}),
exclude: ['node_modules']
});
module.exports = {
entry: [
'./src/index.js'
],
output: {
publicPath: './',
path: path.join(__dirname, 'public'),
filename: '[chunkhash].js'
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders
},
plugins: [
new WebpackCleanupPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
drop_console: true,
drop_debugger: true
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new ExtractTextPlugin({
filename: 'style.css',
allChunks: true
}),
new HtmlWebpackPlugin({
template: './index.html',
files: {
css: ['style.css'],
js: ['bundle.js'],
}
})
]
};
But if I run it on the local server, there is not such a problem:
"use strict";
var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var DashboardPlugin = require('webpack-dashboard/plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const HOST = process.env.HOST || "127.0.0.1";
const PORT = process.env.PORT || "3000";
loaders.push({
test: /\.scss$/,
loaders: ['style-loader', 'css-loader?importLoaders=1', 'sass-loader'],
exclude: ['node_modules']
});
module.exports = {
entry: [
'react-hot-loader/patch',
'./src/index.js', // your app's entry point
],
devtool: process.env.WEBPACK_DEVTOOL || 'eval-source-map',
output: {
publicPath: '/',
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders
},
devServer: {
contentBase: "./public",
// do not print bundle build stats
noInfo: true,
// enable HMR
hot: true,
// embed the webpack-dev-server runtime into the bundle
inline: true,
// serve index.html in place of 404 responses to allow HTML5 history
historyApiFallback: true,
port: PORT,
host: HOST
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.HotModuleReplacementPlugin(),
new ExtractTextPlugin({
filename: 'style.css',
allChunks: true
}),
new DashboardPlugin(),
new HtmlWebpackPlugin({
template: './index.html',
files: {
css: ['style.css'],
js: [ "bundle.js"],
}
}),
]
};
I also tried it on a node.js web server and got the same problem.
Is the contentBase: "./public" made it?
thanks
coolshare

With Vincent's suggestion, I added some redirects to my Apache config
DocumentRoot /var/www
Redirect permanent /main http://...com/
This work partially: Apache did route http://...com/main to http://...com/
The problem is that from then on React takes over but it routes it to the login screen instead of what I expected - stay in the current screen http://...com/main when reload the browser page.
"If you route this URL to your index.html then the JS router will activate and display the correct page." is not true: I tried
Redirect permanent /main http://...com/index.html
and Apache did not recognize the http://...com/index.html
But Apache
It seems no way to route it to the main page...

Related

Refresh page on react-router outputs blank page, bundle.js is not loaded

I can access all paths as long as I do not refresh the page. When I refresh the page however, I get a blank page with no errors. I have looked through a number of answers related to this issue, but I was unable to find a solution that worked for my setup.
I'm using react-router and express, I have set historyApiFallback: true and publicPath: '/'. I checked the console after the page is refreshed, and there is no bundle.js loaded, only index.html is loaded - which explains the blank page. I hope someone can shed some light on this.
folder structure
public
-- index.html
src
-- client
-- server
-- index.js
webpack.config.js
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: ['babel-polyfill', './src/client/index.js'],
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js',
publicPath: '/'
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true
}
},
'sass-loader'
],
include: /\.module\.scss$/
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.scss$/
},
{
test: /\.(png|woff|woff2|eot|ttf|svg|gif)$/,
loader: 'url-loader?limit=100000'
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
devServer: {
port: 3000,
open: true,
proxy: {
'/': 'http://localhost:8080'
},
historyApiFallback: true
},
plugins: [
new CleanWebpackPlugin([outputDirectory]),
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico'
})
]
};
server.js
require('dotenv').config()
const express = require('express')
const { logger, session } = require('./loader')
const auth = require('./api/middleware/authentication')
const { authRoute, sendRoute } = require('./api/routes')
const app = express();
app.use(session);
app.use(express.json())
app.use(express.static('dist'))
// API routes
app.use('/auth', authRoute)
app.use('/send', auth, sendRoute)
app.listen(process.env.PORT || 8080, () => {
logger.info(`Listening on port ${process.env.PORT || 8080}!`)
})
App.js snippet
<Switch>
<Route path="/" exact><LandingPage /></Route>
<Route path="/login">
<Container maxWidth="lg" className={classes.container}>
<Login />
</Container>
</Route>
<Route path="/end"><EndPage /></Route>
</Switch>
I was having the same problem and was looking for a solution. I tried some stuff and it works in production and dev mode now. Dev mode got it to work by adding the historyApiFallback: true and publicPath: '/' (which you already have). For production mode see below. I am just a beginner and this is my first answer ever so please feel free to correct me.
In your express server file server.js (or index.js), I added this:
const path = require('path');
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "path/to/your/index.html"), function (err) {
if (err) {
res.status(500).send(err);
}
});
});
Then, in the public index.html below the "root" div I added:
<script src="/bundle.js"></script>
I think it works because it'll send all requests back to index.html which now has that script of bundle.js but I'm not sure. On the main / page of the website it doesn't load the bundle.js twice which is cool.

React Router not working correctly

I am new to react and have no idea why this wouldnt be working.
My router code is here:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
import HomePage from './components/home/HomePage';
export default (
<Route path="/" component={App}>
<IndexRoute component={HomePage} />
<Route path="example" component={HomePage} />
</Route>
);
So what happens with this code is:
localhost/ displays fine
localhost/example does not display
localhost/example link from the header on the homepage displays, but pressing refresh does not
Thanks for any help, I appreciate it!
here is my webpack config:
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
context: path.join(__dirname, "/client"),
entry: [
'webpack-dev-server/client?http://127.0.0.0:5000/',
'webpack/hot/only-dev-server',
'react-hot-loader/patch',
'./src/index.js'
],
output: {
path: path.join(__dirname, "/client/dist"),
filename: '[name].js',
publicPath: '/'
},
resolve: {
extensions: ['.js', '.jsx', '.json']
},
devServer: {
historyApiFallback: true
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [ 'babel-loader', 'react-hot-loader/webpack' ],
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './static/template.html',
inject: 'body',
filename: 'index.html'
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
]
};
app.js snippet:
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(__dirname + 'client/dist'));
app.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
hot: true,
historyApiFallback: true,
stats: {
colors: true
}
}));
I am using localhost:5000 . The question linked did not fix my problem
FYI, Starting in version 4.0, React Router no longer uses the IndexRoute.
Also for your path, change "example" to "/example"

React Router not behaving as I would like it to

I have a webpack, react, flux, react router setup. I have these two routes:
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={Photos} onEnter={someAuthCheck}>
<Route path="/login" component={Login}></Route>
</Route>
</Router>
),document.getElementById('app'));
When I write http://localhost:8080/login in the browser I get:
Cannot GET /login
rather than my login dialog
I am running on the webpack dev server. What am I not doing right?
My webpack config:
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');
var config = {
resolve: {
alias: {
jquery: "jquery/src/jquery"
}
},
entry: {
main: APP_DIR + '/index.jsx',
},
output: {
publicPath: "/src/client/public/",
path: BUILD_DIR,
filename: '[name].js'
},
module : {
loaders : [
{ test : /\.jsx?/, include : APP_DIR, loader : 'babel-loader' },
{ test: /.(woff|woff2|eot|ttf)$/, loader:"url-loader?prefix=font/&limit=5000" },
{test: /\.(scss|css)$/, loader: ExtractTextPlugin.extract('css-loader!sass-loader')}
]
},
plugins: [
new ExtractTextPlugin("[name].css"),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),
new HtmlWebpackPlugin({
title: 'PhotoTank',
template: 'src/client/app/html-template.ejs',
filename: '../index.html'
})
],
devServer: {
//publicPath: "/src/client/public/",
//historyApiFallBack: true,
// progress: true,
//hot: true,
//inline: true,
// https: true,
//port: 8081,
contentBase: path.resolve(__dirname, 'src/client'),
proxy: {
"/api": {
target: "http://localhost:5000",
pathRewrite: {"^/api" : ""}
}
}
},
};
module.exports = config;
Usually, if you encounter Cannot GET error, it is an issue with webpack-dev-server. From the docs:
To prepare, make sure you have a index.html file that points to your bundle. Assuming that output.filename is bundle.js:
<script src="/bundle.js"></script>
So, you will have to use webpack to generate an index.html file first, usually called npm run build if you are using some boilerplate, or you have to create a separate webpack config for production build to do so.
Alternatively, just create an empty index.html file as instructed above.
There are also some problems with your react-router configuration:
<Route path="/login" component={Login}></Route>
should be
<Route path="login" component={Login}></Route>
There shouldn't be preceding slashes in children routes.
Also, you should be using browserHistory instead of hashHistory if you want to access the page at /login.
You are using hashHistory, so instead of
http://localhost:8080/login
open this:
http://localhost:8080/#/login it will work.
From Doc:
Hash history uses the hash (#) portion of the URL, creating routes
that look like example.com/#/some/path.
Read the difference between hashHistory and browserHistory.

React router + webpack, sub routes not working

I am trying to set up my routers for my app, and have the basic / entry point working (seemingly). It seems when I try to start adding sub routes, it is breaking. I have a pretty straight forward set up right now. I am using react + redux and my render looks like :
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} >
<Route path="/" component={comp1.Component}>
<Route path="test" component={comp2.Component} />
</Route>
</Router>
</Provider>,
// eslint-disable-next-line no-undef
document.getElementById('app')
);
I am running webpack dev server on localhost:8080, and it serves the first route with no problem, however when I go to localhost:8080/test, I am getting a Cannot GET /test .
Here is my webpack config:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./client/app.jsx'
],
output: {
path: path.join(__dirname, ''),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
],
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "babel-loader",
include: __dirname,
query: {
presets: [ 'es2015', 'react', 'react-hmre' ]
}
}]
}
}
Unsure what I am doing wrong here, would be grateful for any help. Thanks!
React Router uses the HTML5 history API. This means that 404 responses need to serve /index.html.
The docs mention how this works. You need to add this to your module.exports object:
devServer: {
historyApiFallback: true
}
Note that this only works for the CLI, when using the Node.js API you need to add this as a second parameter:
var server = new WebpackDevServer(compiler, { historyApiFallback: true });

Webpack server configuration + external libs

I have the following webpack configuration file:
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const LiveReloadPlugin = require('webpack-livereload-plugin');
const path = require('path');
module.exports = {
entry: [
'webpack-dev-server/client?http://0.0.0.0:2000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./app/index.tsx'
],
output: {
path: __dirname + '/dist/',
filename: 'bundle.js'
},
devtool: 'source-map',
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js']
},
module: {
loaders: [
{
test: /\.tsx?$/,
loaders: ['react-hot', 'ts'],
include: path.join(__dirname, 'app')
}
],
preLoaders: [
'source-map-loader'.
{test: /\.js$/, loader: 'source-map-loader'}
]
},
plugins: [
new CopyWebpackPlugin([
{from: './app/index.html', to: './dist/index.html'}
]),
new webpack.HotModuleReplacementPlugin()
],
builds.
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
};
and here is my server configuration:
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
contentBase: './dist',
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
open: 'http://localhost:2000'
}).listen(2000, 'localhost', function (err, result) {
if (err) {
return console.log(err);
}
console.log('Listening at http://localhost:2000/');
});
I want to be able to access the application from root path: http://localhost:2000 and not http://localhost:2000/dist.
One more thing, is there any way to move all the external dependancies from node_modules to dist with webpack (without the need to include the script tag in the index.html file)?
First of all for set application start point you need to set publicPath to "/" or publicPath: 'http://localhost:2000'
Your second question
Is there any way to move all the external dependancies from node_modules to dist with webpack?
Yes
You can use different file just for external modules and bundle that file. You don't need to take care of index.html file let webpack plugin HtmlWebpackPlugin take care of it.
First step set entry points for your app
entry: {
'vendors': './src/vendors.ts',//your external libraries
'app': './src/main.ts' //your app
}
and out put
output: {
publicPath: '/',
filename: '[name].js'//this will generate two different files app.js, vendor.js
}
How to add HtmlWebpackPlugin?
Add this in you plugins
new HtmlWebpackPlugin({
template: "./src/index.html",
minify:false
})
Now it will place script tags for you
on you server configuration change your public path to
publicPath: '/',

Resources