React router + webpack, sub routes not working - reactjs

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 });

Related

Cannot GET "/About" with react-router v4 (Production Help)

I've been reading over all the docs for react-router-dom (v4), and tons of Stack Overflow questions with my same error, but A) They leave a lot of unanswered holes and B) They seem to all be suggesting a development only fix, so I'm hoping to see what people are actually doing in PRODUCTION for this simple scenario and C) I'm probably doing something stupid and the answers aren't working for me, with the error "Cannot GET /about" rendering with no errors in the console.
I'm using Express, React, Node and using Webpack for compiling. I can successfully reach my homepage, and clicking any links takes me to the appropriate components, but manually typing in the URL breaks this, as discussed here and the reasons for this error discussed here.
Most answers suggest adding devServer.historyApiFallback = true and output.publicPath = '/' in the webpack.config.js file, which implies I also need to run npm install --save-dev webpack-dev-server and run it using node_modules/.bin/webpack-dev-server as suggested in the docs. Doing all of this, nothing happens. In fact, it's worse now because I also can't access my home route of '/'.
So before dropping my current config here, 1) What can I do to fix this? 2) Does it even matter? The webpack-dev-server is obviously for development only so what about production?
My webpack.config.js file:
var webpack = require('webpack');
var path = require('path');
var envFile = require('node-env-file');
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
try {
envFile(path.join(__dirname, 'config/' + process.env.NODE_ENV + '.env'))
} catch (e) {
}
module.exports = {
devServer: {
historyApiFallback: true,
},
entry: [
'script-loader!foundation-sites/dist/js/foundation.min.js',
'./app/app.jsx'
],
plugins: [
new webpack.DefinePlugin({
'process.env': {
//you don't get to see this
}
})
],
output: {
path: __dirname,
filename: './public/bundle.js',
publicPath: '/'
},
resolve: {
modules: [
__dirname,
'node_modules',
'./app/components',
'./app/api'
],
alias: {
app: 'app',
applicationStyles: 'app/styles/app.scss',
actions: 'app/actions/actions.jsx',
configureStore: 'app/store/configureStore.jsx',
reducers: 'app/reducers/reducers.jsx'
),
},
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
},
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/
},
{
loader: 'url-loader?limit=100000',
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/
},
{
loader: 'sass-loader',
test: /\.scss$/,
options: {
includePaths: [
path.resolve(__dirname, './node_modules/foundation-sites/scss')
]
}
}
]
},
devtool: process.env.NODE_ENV === 'production' ? false : 'source-map'
};
My app.jsx:
var React = require('react');
var ReactDOM = require('react-dom');
import {Provider} from 'react-redux';
import {BrowserRouter as Router, Route, Switch, Link, HashRouter} from 'react-router-dom';
import Home from 'Home';
import Watch from 'Watch';
import About from 'About';
import AddShow from 'AddShow';
var store = require('configureStore').configure();
import firebase from 'app/firebase/';
// Load Foundation
$(document).foundation();
// App css
require('style-loader!css-loader!sass-loader!applicationStyles');
ReactDOM.render(
<Provider store={store}>
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/watch" component={Watch}/>
<Route path="/about" component={About}/>
<Route path="/addshow" component={AddShow}/>
</div>
</Router>
</Provider>,
document.getElementById('app')
);
You have to set up your web server (the one that serves index.html with the react app) to redirect all requests to the url of your index.html so that react-router can do its job. That's what the suggested change to webpack.config.js is doing for webpack-dev-server
In your webpack.config.js you need to enable the html plugin so webpack knows where your index.html is:
plugins: [
new webpack.DefinePlugin({
'process.env': {
//you don't get to see this
}
}),
new HtmlWebpackPlugin({
template: 'public/index.html' //or wherever your index.html is
})
],

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"

Routing issue in React with Webpack

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...

React router cannot get my entry js

Hello i have a problem with react-router, my code
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={StartPage}/>
<Route path="/matches" component={MatchesPage} />
<Route path="/sector/:idparam" component={SectorsPage} />
</Route>
</Router>
</Provider>,
app);
When I call /matches everything is OK, but when i try GET /sector/15 app failed try to load http://localhost:8080/sector/client.min.js but normally will load from default path (/)
Webpack:
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
module.exports = {
context: path.join(__dirname, "src"),
devtool: debug ? "inline-sourcemap" : null,
entry: "./js/client.js",
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-decorators-legacy', 'transform-class-properties'],
}
}
]
},
output: {
path: __dirname + "/src/",
filename: "client.min.js"
},
plugins: debug ? [] : [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({mangle: false, sourcemap: false}),
],
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
},
};
I don't think the issue is with react router, or even webpack. I think your issue is related to how you are requiring your client js file, although since you didn't include that section of code I cannot confirm.
It looks like you are including the script relative to your path
(<script src="client.min.js"></script> no leading slash) instead of an absolute path (<script src="/client.min.js"></script>).

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.

Resources