Problem
I’m using React-router, and it works fine when I click on link buttons, but it doesn’t load what I want when I reload my website.
For example, I’m in localhost/joblist and everything is good because I came here by clicking on a link. However, if I reload the page, I receive the following:
Cannot GET /joblist
It didn’t function like this by default. Initially, I used localhost/#/ and localhost/#/joblist as my URLs, and they worked great. However, I dislike this type of URL, therefore in an attempt to remove the #, I wrote:
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.body);
});
This problem does not occur with localhost/, which always delivers the desired result.
EDIT: Because this app is single-page, there is no need for /joblist to query any servers.
EDIT2: I’ve replaced my entire router.
var routes = (
<Route name="app" path="/" handler={App}>
<Route name="joblist" path="/joblist" handler={JobList}/>
<DefaultRoute handler={Dashboard}/>
<NotFoundRoute handler={NotFound}/>
</Route>
);
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.body);
});
Asked by DavidDev
Solution #1
Given the comments on the acceptable response and the generic nature of the question (‘doesn’t work,’ I though this would be a good place to provide some general explanations regarding the issues at hand. As a result, this answer serves as background information / elaboration on the OP’s specific use case. Please bear with me for a moment.
The first thing to realize is that the URL is now interpreted in two locations, whereas there used to be only one in ‘the good old days.’ When life was simpler, a user would send a request to the server for http://example.com/about, which would analyze the path component of the URL, decide that the user was seeking the about page, and then return that page.
Things are more complicated with client-side routing, which React-Router provides. The client doesn’t have any JS code loaded at first. As a result, the server will always be the first request. This will then produce a page with the required script tags to load React and React Router, among other things. Phase 2 begins only after the scripts have loaded. When a user clicks the ‘About us’ navigation link in phase 2, the URL is updated locally only to http://example.com/about (thanks to the History API), but no request to the server is made. React Router, on the other hand, works on the client side, determining which React view to render and rendering it. If your about page doesn’t have any information on it,
When you click a link, some Javascript is executed, which manipulates the URL in the address bar without prompting a page refresh, allowing React Router to do a client-side page transition.
Consider what happens if you copy the URL into the address bar and send it to a friend via e-mail. Your website has not yet been loaded by your friend. To put it another way, she’s still in phase 1. Her system does not yet have a React Router installed. As a result, her browser will send a request to http://example.com/about.
This is where your problems begin. Until today, you could get away with simply storing a static HTML file in your server’s webroot. However, when requesting other URLs from the server, this would result in 404 errors. Those exact URLs function well on the client-side because React Router takes care of the routing, but they won’t work on the server-side unless you tell it to.
You’ll need to set up routes on both the server and client sides if you want the http://example.com/about URL to work. Isn’t that correct?
And it’s at this point that you’ll have to make a decision. The problem can be avoided entirely by using a catch-all route that returns the bootstrap HTML, or the full-on isomorphic method, in which both the server and the client run the same JS code.
Your about page URL would look like this if you used Hash History instead of Browser History: http://example.com/#/about After the hash (#) symbol, nothing is delivered to the server. As a result, the server just sees http://example.com/ and, as expected, sends the index page. The #/about section will be picked up by React-Router, and the correct page will be displayed.
Downsides:
You still utilize Browser History, but instead of sending /* to index.html, you set up a catch-all on the server that sends /* to index.html, thereby giving you the same result as Hash History. You do, however, have clean URLs, and you may improve this scheme later without invalidating all of your users’ favorites.
Downsides:
In the hybrid technique, you add customized scripts for various routes to the catch-all scenario. You might write some simple PHP scripts to return your site’s most significant pages, complete with content, so Googlebot can at least see what’s there.
Downsides:
What if we use Node JS as our server, and the identical JS code runs on both ends? We no longer need to repeat our rendering code because we have all of our routes set in a single react-router config. In a sense, this is ‘the holy grail.’ The server delivers the exact same markup as we’d get if the page transition occurred on the client. In terms of SEO, this method is ideal.
Downsides:
Choose the option that will allow you to get away with it. Personally, I believe the catch-all is easy to put up, thus that would be my minimum requirement. This configuration allows you to gradually enhance your skills. I’d look at making an isomorphic app if you’re already using Node JS as your server platform. Yes, it’s difficult at first, but it’s actually a very elegant solution to the problem once you get the feel of it.
So, in essence, it would be the decisive factor for me. I’d go isomorphic if my server runs Node JS; otherwise, I’d go with the Catch-all solution and then expand on it (Hybrid solution) as time goes on as SEO requirements dictate it.
There are some nice tutorials on the subject if you’d like to learn more about isomorphic (also known as ‘universal’) rendering with React:
I also recommend checking into various starting kits to get you started. Choose one that corresponds to your technology stack preferences (remember, React is just the V in MVC, you need more stuff to build a full app). Take a look at the one that Facebook has published:
Alternatively, you can choose from a variety of options provided by the community. There’s currently an excellent site that tries to index them all:
I began with the following:
I’m currently utilizing a homebrew universal rendering system that was inspired by the two starter kits mentioned above, but it’s already outdated.
Best of luck in your endeavor!
Answered by Stijn de Witt
Solution #2
The responses here are all incredibly useful; nevertheless, I found that customizing my Webpack server to expect the routes worked best for me.
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
},
For me, the historyApiFallback was the solution. Now the routing is working properly, and I can either refresh the page or directly type in the URL. On your node server, there’s no need to be concerned about workarounds. This solution is clearly only applicable if you’re using webpack.
EDIT: see my answer here for a more detailed reason why this is necessary: https://stackoverflow.com/a/37622953/5217568
Answered by jmancherje
Solution #3
You can update your.htaccess file to include the following:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
I’m using react version 16.12.0 and react-router version 5.1.2. This is the Catch-all strategy, and it’s probably the simplest way to get started.
Answered by BrahimS
Solution #4
For React Router V4 Users:
If you use the Hash History technique discussed in previous responses to fix this problem, keep in mind that
<Router history={hashHistory} >
Doesn’t work in V4; instead, use HashRouter:
import { HashRouter } from 'react-router-dom'
<HashRouter>
<App/>
</HashRouter>
Reference: HashRouter
Answered by user2875289
Solution #5
I recently used create-react-app to build a website and ran into the same problem as described here. I utilize the react-router-dom package’s BrowserRouting. I’m on a Nginx server, and adding the following to /etc/nginx/yourconfig.conf solved the problem for me.
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.html break;
}
}
In the case of Appache, this equates to adding the following to the.htaccess file.
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
This appears to be the Facebook-suggested solution, which may be found here.
Answered by Aidin
Post is based on https://stackoverflow.com/questions/27928372/react-router-urls-dont-work-when-refreshing-or-writing-manually