Hi
I don't know exactly is this issue with webpack but i think so
I have simple config for router
var ReactappApp = require('./ReactappApp');
var HelloWorld = require('./HelloWorld');
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;
var content = document.getElementById('content');
var Routes = (
<Route path="/" handler={ReactappApp}>
<Route name="hello" path="hello" handler={HelloWorld}/>
</Route>
);
console.log(Routes);
Router.run(Routes,Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, content);
});
So i try to load HelloWorld component at _/hello_ and get Cannot GET /hello
So server doesn't see this route
I use grunt and connect also.
Here is grunt task for webpack:
'webpack-dev-server': {
options: {
hot: true,
port: 8000,
webpack: webpackDevConfig,
publicPath: '/assets/',
contentBase: './<%= pkg.src %>/'
}
and here is webpack config
https://gist.github.com/fellz/b2cc0de81c7b1f5bf442#file-webpack-config
with _historyApiFallback: true_
'webpack-dev-server': {
options: {
hot: true,
port: 8000,
webpack: webpackDevConfig,
publicPath: '/assets/',
contentBase: './<%= pkg.src %>/',
historyApiFallback: true
}
it's loading ReactappApp instead of HelloWorld
p.s. react-router-component works as expected and perfectly well
This is not the router's problem: Webpack just doesn't know you're using pushstate and tries to actually serve /hello
from filesystem.
The proper way to do that is to run pushstate server separately and set contentBase
to its port.
A hackish but simpler way to do that is to inject a middleware (note this relies on undocumented property and may stop working in future):
server = new WebpackDevServer(webpack(config), {
contentBase: contentBase,
publicPath: config.output.publicPath,
hot: true
});
server.app.use(function pushStateHook(req, res, next) {
var ext = path.extname(req.url);
if ((ext === '' || ext === '.html') && req.url !== '/') {
req.pipe(request(localURL)).pipe(res);
} else {
next();
}
});
Can't say how to translate this to grunt task tho.
@gaearon
Ok thanks
btw why react-router-components works fine with this stuff ?
I don't know, it shouldn't. If server doesn't serve index.html
on /hello
, there's nothing any router can do about it.
@gaearon well key diff in this historyApiFallback: true react-router-component works fine with this option and react-router is not ...
I'm not sure what historyApiFallback
is (couldn't find it in react-router-component docs) but you can fall back to Router.HashLocation
if you'd like.
@gaearon historyApiFallback
is an option for webpack-dev-server that you familiar with ) look at the last line
'webpack-dev-server': {
options: {
hot: true,
port: 8000,
webpack: webpackDevConfig,
publicPath: '/assets/',
contentBase: './<%= pkg.src %>/',
historyApiFallback: true
}
Ah, thanks. I didn't know dev server supports this. So you're saying it doesn't work with RR but works with RRC?
@gaearon exactly
I'll take a look, thanks for reporting!
@gaearon do you know what the core issue is with historyApiFallback: true
?
I'll take a look now, sorry for delay
I couldn't reproduce the problem. In my testing, historyApiFallback
works fine with RR.
I find historyApiFallback works with shallow routes but not deep routes, I'm not sure it has anything to do with react-router though.
The reason seems to be requests to my app.js are not resolved to the root like other requests are?
GET http://localhost:8080/orgs/-Jfah70YYREFxUpYaG4C/endpoints/app.js fails?
I found a problem there
have to configure routes properly like this:
var routes = [
<Route path="/" handler={App} />,
<Route path="/hello" handler={Hello} />
]
but still RR doesn't work if you want your routes without hashes (Router.HistoryLocation)
historyApiFallback: true
flag in webpack-dev-server
options must be setp.s. full project with code is here https://github.com/fellz/react-webpack-app
https://github.com/webpack/react-starter uses pushstate and the router, so something must be up with your app. Let us know if you find what's wrong :)
I just ran into this issue. You have to tell webpack to always serve up index.html. Here is an example, assuming your server.js file and index.html are both in the root of your project:
server.js
var webpack = require('webpack'),
WebpackDevServer = require('webpack-dev-server'),
config = require('./webpack.config'),
path = require("path");
var server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
});
// Important part. Send down index.html for all requests
server.use('/', function(req, res) {
res.sendFile(path.join(__dirname+'/index.html'));
});
server.listen(3010, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:3010');
});
If you're like me and use html-webpack-plugin
this works wonderfully:
var config = require('./webpack.config');
var http = require('http');
var path = require('path');
var url = require('url');
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var server = new WebpackDevServer(webpack(config), config.devServer);
// Important part. Send down index.html for all requests
server.use('/', function (req, resp, next) {
var opts = url.parse('http://localhost:8080');
opts.method = req.method;
opts.headers = req.headers;
var myReq = http.request(opts, function (myRes) {
var statusCode = myRes.statusCode;
var headers = myRes.headers;
var location = headers.location;
if (200 !== statusCode) {
next();
return;
}
resp.writeHead(myRes.statusCode, myRes.headers);
myRes.on('error', function (err) {
next(err);
});
myRes.pipe(resp);
});
myReq.on('error', function (err) {
next(err);
});
if (!req.readable) {
myReq.end();
} else {
req.pipe(myReq);
}
});
server.listen(8080, 'localhost', function() {
console.log('Listening on http://localhost:' + server.get('port'));
});
@nelix I ran into a similar issue. I am not yet using react-router, but the symptom matched to what is being described here. In my case, setting historyApiFallback: true
worked when when the root url is loaded first, when I loaded some deep url, browser failed to load a few fonts. https://github.com/webpack/webpack/issues/443 gave me a hint so I tried adding __webpack_public_path__ = "/"
in my main entry point. That fixed the deep link reload problem.
Thanks @ramnivas
Some of the examples above are very complicated. In most cases you can get this working simply by adding the following lines to your webpack.config.js
file:
devServer: {
historyApiFallback: true
}
I made an example app to help anyone else looking for a solution to this (like I was).
@adrianmacneil seems that react's about "complexity first" sometimes (:
thanks a lot for pointing this out
@adrianmacneil As mentioned above, that only seems to work for shallow routes. For example, it will work for /foo
or /bar
but not /foo/bar
, unless of course you first visit /foo
and then navigate to /foo/bar
via a Link
.
It works fine for me with nested routes. Try the example app above - I just added a nested route to test it.
@dmwyatt, :+1: on that. I've got a simple case that success for one level deep, but fails beyond that. (Any chance you're lazy-loading files with require.ensure
?)
@michaelahlers No, I'm not using require.ensure
. However, the example app provided by @adrianmacneil works correctly. I cannot find a difference between what he is doing and what my much more complicated app is doing.
module.exports = {
entry: './js/main.js',
output: {
path: __dirname + '/js',
filename: 'bundle.js',
publicPath: '/'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel-loader']
}
]
},
node: {
net: 'mock',
dns: 'mock',
fs: 'empty'
},
devtool: 'source-map',
devServer: {
historyApiFallback: true
}
};
With this config my app is now finally loading all routes...but it does a complete page load for each route transition unless I switch to hash routes.
@adrianmacneil's solution worked for me. :-)
I've been struggling with this for longer than I'd like to admit, but I've just figured it out.
Besides enabling historyApiFallback
, if you're having trouble _only_ with nested routes, check your html file's reference to your script.
In my case, it was the difference between <script src="/bundle.js"></script>
and <script src="bundle.js"></script>
causing problems on nested routes. Basic stuff, and super obvious now that I've found it, but it was easy to miss.
Thanks to @adrianmacneil for the sample app, it was a big help.
edit - "/bundle.js" being the correct src, so the reference isn't taken as relative to the current path.
@BenFlanagan thanks a lot. Completely agree with it:
Basic stuff, and super obvious now that I've found it, but it was easy to miss.
@dmwyatt or anybody else, did you find a way to stop the complete page load every time the route changes ?
In addition to @BenFlanagan's excellent observation:
Besides enabling
historyApiFallback
, if you're having trouble only with nested routes, check your HTML file's reference to your script.
When using the HTML Webpack Plugin to generate your bootstrap page, you'll be able to enforce absolute paths by adding a publicPath
to your Webpack configuration:
{
// ...
output: {
// ...
publicPath: '/',
// ...
}
// ...
}
Incidentally, this should be useful for those encountering issue #113 in react-boilerplate.
@BenFlanagan Adding that single slash fixed a running 2 month bug we had with (only) our nested routes and hot-reloading. Thank you so much!
@BenFlanagan That was it for me :)
@michaelahlers and @BenFlanagan summed up solved the issue here.
Plugins work so entwined that simple problems turn into mazes real quickly.
Thanks !
:+1:
@xuorig, I'm still struggling with full page reload on route navigation. Have you succeeded to solve it? Any ideas how that can be fixed?
@Dmitry-N-Medvedev don't know if it's your case but if you have modified the output.publicPath you should specify the redirect URL.
// output.publicPath: '/foo-app/'
historyApiFallback: {
index: '/foo-app/'
}
@BenFlanagan solved this problem for me. But then I had issues resolving some of my static resources.
I added <base href="/">
to the head of my index.html and it resolved my issue. This allowed me to leave off the leading / from the build script path. Nested routes correctly resolve and my static resources are loaded.
@dmwyatt did you fix the problem with nested urls? i fixed it with publicPath but now reloads full page instead components :'(
@OscarGalindo
const path = require('path');
const fs = require("fs");
{
devServer: {
historyApiFallback: false,
setup: function (app) {
app.use(function pushStateHook(req, res, next) {
var ext = path.extname(req.url);
if ((ext === '' || ext === '.html') && req.url !== '/') {
res.setHeader("Content-Type", "text/html");
fs.createReadStream(helpers.root('dist-dev/index.html')).pipe(res);
} else {
next();
}
});
}
}
}
This issue comes up in google a lot, so this might come in handy to anyone having trouble with this: historyApiFallback
will not work well if you are using certain proxy
configurations.
I don't know how to fix it other than to remove the proxy
option from your dev server configuration.
@sunjay consult http://stackoverflow.com/a/36139331/1615123. Order of proxy and historyApiFallback options in the devServer config matters.
Ran into this issue today. I had to set historyApiFallback: true
to get rid of the 404 message. However the Router
was still resolving to /
instead of the actual route (/redirect
in my case). I am working on a react-native
project wrapped in react-native-web
and I was using the react-router-native
router for both web and native. Switching to react-router-dom
for web fixed the weird Router
behaviour for me in the end. 馃帀
Most helpful comment
I've been struggling with this for longer than I'd like to admit, but I've just figured it out.
Besides enabling
historyApiFallback
, if you're having trouble _only_ with nested routes, check your html file's reference to your script.In my case, it was the difference between
<script src="/bundle.js"></script>
and<script src="bundle.js"></script>
causing problems on nested routes. Basic stuff, and super obvious now that I've found it, but it was easy to miss.Thanks to @adrianmacneil for the sample app, it was a big help.
edit - "/bundle.js" being the correct src, so the reference isn't taken as relative to the current path.