ChatGPT解决这个技术问题 Extra ChatGPT

React-router URLs don't work when refreshing or writing manually

I'm using React-router and it works fine while I'm clicking on link buttons, but when I refresh my webpage it does not load what I want.

For instance, I am in localhost/joblist and everything is fine because I arrived here pressing a link. But if I refresh the webpage I get:

Cannot GET /joblist

By default, it didn't work like this. Initially I had my URL as localhost/#/ and localhost/#/joblist and they worked perfectly fine. But I don't like this kind of URL, so trying to erase that #, I wrote:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

This problem does not happen with localhost/, this one always returns what I want.

This app is single-page, so /joblist doesn't need to ask anything to any server.

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);
});
unless you use htaccess to load your main touring page and tell your router to use location.pathname it won't work..
How did you erase that # symbol? Thank you!
If you are hosting your react app in an S3 bucket, you can simply set the error document to index.html. This will make sure index.html is hit no matter what.
In my case, it works fine in windows but not in linux
This is the reference that helped solve my problem: github.com/facebook/create-react-app/blob/master/packages/…

S
Stijn de Witt

Server-side vs Client-side

The first big thing to understand about this is that there are now 2 places where the URL is interpreted, whereas there used to be only 1 in 'the old days'. In the past, when life was simple, some user sent a request for http://example.com/about to the server, which inspected the path part of the URL, determined the user was requesting the about page, and then sent back that page.

With client-side routing, which is what React Router provides, things are less simple. At first, the client does not have any JavaScript code loaded yet. So the very first request will always be to the server. That will then return a page that contains the needed script tags to load React and React Router, etc. Only when those scripts have loaded does phase 2 start. In phase 2, when the user clicks on the 'About us' navigation link, for example, the URL is changed locally only to http://example.com/about (made possible by the History API), but no request to the server is made. Instead, React Router does its thing on the client-side, determines which React view to render, and renders it. Assuming your about page does not need to make any REST calls, it's done already. You have transitioned from Home to About Us without any server request having fired.

So basically when you click a link, some JavaScript runs that manipulates the URL in the address bar, without causing a page refresh, which in turn causes React Router to perform a page transition on the client-side.

But now consider what happens if you copy-paste the URL in the address bar and e-mail it to a friend. Your friend has not loaded your website yet. In other words, she is still in phase 1. No React Router is running on her machine yet. So her browser will make a server request to http://example.com/about.

And this is where your trouble starts. Until now, you could get away with just placing a static HTML at the webroot of your server. But that would give 404 errors for all other URLs when requested from the server. Those same URLs work fine on the client-side, because there React Router is doing the routing for you, but they fail on the server-side unless you make your server understand them.

Combining server- and client-side routing

If you want the http://example.com/about URL to work on both the server- and the client-side, you need to set up routes for it on both the server- and the client-side. It makes sense, right?

And this is where your choices begin. Solutions range from bypassing the problem altogether, via a catch-all route that returns the bootstrap HTML, to the full-on isomorphic approach where both the server and the client run the same JavaScript code.

Bypassing the problem altogether: Hash History

With Hash History, instead of Browser History, your URL for the about page would look something like this: http://example.com/#/about

The part after the hash (#) symbol is not sent to the server. So the server only sees http://example.com/ and sends the index page as expected. React Router will pick up the #/about part and show the correct page.

Downsides:

'ugly' URLs

Server-side rendering is not possible with this approach. As far as search engine optimization (SEO) is concerned, your website consists of a single page with hardly any content on it.

Catch-all

With this approach, you do use the Browser History, but just set up a catch-all on the server that sends /* to index.html, effectively giving you much the same situation as with Hash History. You do have clean URLs however and you could improve upon this scheme later without having to invalidate all your user's favorites.

Downsides:

More complex to set up

Still no good SEO

Hybrid

In the hybrid approach, you expand upon the catch-all scenario by adding specific scripts for specific routes. You could make some simple PHP scripts to return the most important pages of your site with content included, so Googlebot can at least see what's on your page.

Downsides:

Even more complex to set up

Only good SEO for those routes you give the special treatment

Duplicating code for rendering content on server and client

Isomorphic

What if we use Node.js as our server so we can run the same JavaScript code on both ends? Now, we have all our routes defined in a single react-router configuration and we don't need to duplicate our rendering code. This is 'the holy grail' so to speak. The server sends the exact same markup as we would end up with if the page transition had happened on the client. This solution is optimal in terms of SEO.

Downsides:

Server must (be able to) run JavaScript. I've experimented with Java in conjunction with Nashorn, but it's not working for me. In practice, it mostly means you must use a Node.js based server.

Many tricky environmental issues (using window on server-side, etc.)

Steep learning curve

Which should I use?

Choose the one that you can get away with. Personally, I think the catch-all is simple enough to set up, so that would be my minimum. This setup allows you to improve on things over time. If you are already using Node.js as your server platform, I'd definitely investigate doing an isomorphic app. Yes, it's tough at first, but once you get the hang of it it's actually a very elegant solution to the problem.

So basically, for me, that would be the deciding factor. If my server runs on Node.js, I'd go isomorphic; otherwise, I would go for the Catch-all solution and just expand on it (Hybrid solution) as time progresses and SEO requirements demand it.

If you'd like to learn more about isomorphic (also called 'universal') rendering with React, there are some good tutorials on the subject:

React to the future with isomorphic apps

The Pain and the Joy of creating isomorphic apps in ReactJS

How to Implement Node + React Isomorphic JavaScript & Why it Matters

Also, to get you started, I recommend looking at some starter kits. Pick one that matches your choices for the technology stack (remember, React is just the V in MVC, you need more stuff to build a full app). Start with looking at the one published by Facebook itself:

Create React App

Or pick one of the many by the community. There is a nice site now that tries to index all of them:

Pick your perfect React starter project

I started with these:

React Isomorphic Starterkit

React Redux Universal Hot Example

Currently, I am using a homebrewed version of universal rendering that was inspired by the two starter kits above, but they are out of date now.

Good luck with your quest!


Great post Stijn! Would you recommend going with a starter kit for an Isomorphic react app? If so, could you give an example of one you would prefer?
@Paulos3000 It depends on what server you are using. Basically you define a route for /* and make it respond with your HTML page. The tricky thing here is to make sure that you don't intercept requests for the .js and .css files with this route.
@Paulos3000 See here for some related questions: for Apache/php, for Express/js, for J2E/Java.
@Stijn de Witt This is a great clarification and well answered on this topic. However, what are the options when your app could be served within a sub directory (by iis for example). Ie: domain/appName/routeName or domain/appName/subfolder/routeName
@LeonGaban It seems since this answer was written, React Router changed their implementation. They now have different Router instances for the different histories and they do the configuring of the history in the background. The basic principles are the same still.
A
Alan W. Smith

If you are using Apache as your web server, you can insert this into your .htaccess file:

<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 am using react: "^16.12.0" and react-router: "^5.1.2" This method is the Catch-all and is probably the easiest way to get you started.


This works fine! and the easiest if you don't want to restructure your app
Dont forget RewriteEngine On as the first line
Note this answer is referring to config on an Apache server. This is not part of a React app.
Can anyone help me? Where is this htaccess put? Do you need to npm run build after adding the file?
@TayyabFerozi You have to make sure the path after RewriteBase and after the last RewriteRule matches the folder that the app lives (if it's a subfolder)
P
Peter Mortensen

The answers here are all extremely helpful. Configuring my Webpack server to expect the routes worked for me.

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

The historyApiFallback is what fixed this issue for me. Now routing works correctly and I can refresh the page or type in the URL directly. There isn't any need to worry about workarounds on your Node.js server. This answer obviously only works if you're using Webpack.

See my answer to React-router 2.0 browserHistory doesn't work when refreshing for a more detailed reason why this is necessary.


Do note that the Webpack team recommends against using the dev server in production.
For generic development purposes this is the best solution. historyApiFallback is sufficient. As for all the other options, it can also be set from CLI with the flag --history-api-fallback.
@Kunok It doesn't. This is a quick fix for development but you will still have to figure something out for production.
contentBase :':/' cause your app files can be access from the url
Works for React 17. Thanks
P
Peter Mortensen

For React Router V4 users:

If you try to solve this problem by the Hash History technique mentioned in other answers, note that

<Router history={hashHistory} >

does not work in V4. Please use HashRouter instead:

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

Reference: HashRouter


Could you give any detail as to why the HashRouter fixes this issue? The link you provided doesn't explain it for me. Also, is there a way to hide the hash in the path? I was using the BrowserRouter but ran into this 404 issue with it..
i was using browser router and it was causing 404 error on refresh, but then i replaced browser router with hash router and not it works fine. Thanks
Big thanks to you mate, I really appreciate that. You save me a few working hours 👍
Don't want a hash in my URLs
P
Peter Mortensen

I used Create React App to make a website just now and had the same issue presented here.

I use BrowserRouting from the react-router-dom package. I am running on a Nginx server and adding the following to /etc/nginx/yourconfig.conf solved it for me:

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

Which corresponds to adding the following to the .htaccess in case you are running Apache:

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

This also seems to be the solution suggested by Facebook themselves and can be found here.


Wow, such a simple solution for nginx; works like a charm, as a new nginx user. Just copy-paste and good to go. +1 for linking to the official fb documentation. Way to go.
For the record: I'm using a AWS Instance with nginx and an Angular App. This works.
you save me. i was trying to fix my problem from yesterday i tired couple of solution but i failed. finally you helped me yo solved this router issue. ah thanks a lot brother
Is there anything different needed for "react": "^16.8.6", "react-router": "^5.0.1"? I tried these solutions (both nginx and apache) and they don't work.
As Facebook has not changed its documentation regarding this I can only assume the procedure is the same. I haven't tried it in a while though.
P
Peter Mortensen

In your index.html file's head, add the following:

<base href="/">
<!-- This must come before the CSS and JavaScript code -->

Then, when running with the Webpack development server, use this command.

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback is the important part


It's worth mentioning that if you're using a base href that isn't / then HashRouter won't work (if you're using hash routing)
The tag did it for me. Thanks for this. Webpack dev server kept trying to grab the bundle from the path/ and was failing.
I'm using React.js + Webpack mode. I added --history-api-fallback parameter in package.json file. Then page refreshing is working correctly. Each when I change code, web page is refresh automatically. ``` "scripts": { "start": "rimraf build && cross-env NODE_ENV='development' webpack --mode development && cross-env NODE_ENV=development webpack-dev-server --history-api-fallback", ... } ```
saved the day
P
Peter Mortensen

The router can be called in two different ways, depending on whether the navigation occurs on the client or on the server. You have it configured for client-side operation. The key parameter is the second one to the run method, the location.

When you use the React Router Link component, it blocks browser navigation and calls transitionTo to do a client-side navigation. You are using HistoryLocation, so it uses the HTML5 history API to complete the illusion of navigation by simulating the new URL in the address bar. If you're using older browsers, this won't work. You would need to use the HashLocation component.

When you hit refresh, you bypass all of the React and React Router code. The server gets the request for /joblist and it must return something. On the server you need to pass the path that was requested to the run method in order for it to render the correct view. You can use the same route map, but you'll probably need a different call to Router.run. As Charles points out, you can use URL rewriting to handle this. Another option is to use a Node.js server to handle all requests and pass the path value as the location argument.

In Express.js, for example, it might look like this:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

Note that the request path is being passed to run. To do this, you'll need to have a server-side view engine that you can pass the rendered HTML to. There are a number of other considerations using renderToString and in running React on the server. Once the page is rendered on the server, when your app loads in the client, it will render again, updating the server-side rendered HTML as needed.


excuse Excuse me, may you explain pls the next statement "when you hit refresh, you bypass all of the React and React Router code"? Why does it happen?
React router is normally used to handle different 'paths' within the browser only. There are two typical ways do do this: the older style hash path and the newer history API. A browser refresh will make a server request, which will bypass your client-side react router code. You will need to handle the path on the server (but only if you're using the history API). You can use react router for this, but you'll need to do something similar to what I described above.
got it. But how to be if the server is on non-JS language (let's say Java)?
sorry… you mean that you require an express server to render a route that is "non root" ? I was first amazed that the isomorphism's definition was not including the data fetch in its concept (things are desperately complex if you want to render view WITH data serverside, although it would be an obvious SEO need — and isomorphism claims it). Now I'm like "WTF react", seriously… Correct me if I'm wrong, does ember/angular/backbone require a server thing to render a route ? I really don't understand this bloated requirement to use routes
Does it mean that refresh in react-router doesn't work if my react application is not isomorphic?
P
Peter Mortensen

If you are using Create React App:

There's a great walkthrough of this issue with solutions for many major hosting platforms that you can find here on the Create React App page. For example, I use React Router v4 and Netlify for my frontend code. All it took was adding one file to my public folder ("_redirects") and one line of code in that file:

/*  /index.html  200

Now my website properly renders paths like mysite.com/pricing when entered into the browser or when someone hits refresh.


if you are using Apache HTTP Server, then .htaccess doesn't work, check the apache version because in version 2.3.9 and later the default AllowOverride is None try to change AllowOverride to All. check this answer
P
Peter Mortensen

If you're hosting a React app via AWS Static S3 Hosting and CloudFront

This problem presented itself by CloudFront responding with a 403 Access Denied message, because it expected /some/other/path to exist in my S3 folder, but that path only exists internally in React's routing with React Router.

The solution was to set up a distribution Error Pages rule. Go to the CloudFront settings and choose your distribution. Next, go to the "Error Pages" tab. Click "Create Custom Error Response" and add an entry for 403 since that's the error status code we get.

Set the Response Page Path to /index.html and the status code to 200.

The end result astonishes me with its simplicity. The index page is served, but the URL is preserved in the browser, so once the React application loads, it detects the URL path and navigates to the desired route.

Error Pages 403 Rule


This is exactly what I needed. Thank you.
So.. all your urls work, but return status code 403? It is not correct. Expected status codes should be in the 2xx range.
@StijndeWitt I'm not sure you're understanding correctly. CloudFront will handle everything - the client will not see any 403 status codes. The re-routing happens server-side, renders the index page, maintains the url, and provides the correct route as a result.
thanks for this @th3morg. Does this setup also work for multi-level paths? It works great for me if any path at the root level, like domain/signup, domain/login, domain/, but it doesn't work for multi-level paths, like domain/document/.
Oh you beautiful being! Needed to set it for a 404 response and it worked like a charm!
P
Peter Mortensen

If you are hosting your React application on IIS, just add a web.config file containing:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.webServer>
        <httpErrors errorMode="Custom" existingResponse="Replace">
            <remove statusCode="404" subStatusCode="-1" />
            <error statusCode="404" path="/" responseMode="ExecuteURL" />
        </httpErrors>
    </system.webServer>
</configuration>

This will tell the IIS server to return the main page to the client instead of a 404 error and there isn't any need to use the hash history.


This simply solved our problem, thank you
P
Peter Mortensen

This can solve your problem.

I also faced the same problem in the React application in Production mode. Here are the two solutions to the problem.

Solution 1. Change the routing history to "hashHistory" instead of browserHistory in the place of

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

Now build the app using the command

sudo npm run build

Then place the build folder in your var/www/ folder. Now the application is working fine with the addition of # tag in each and every URL. Like

localhost/#/home
localhost/#/aboutus

Solution 2: Without the # tag using browserHistory,

Set your history = {browserHistory} in your router. Now build it using sudo npm run build.

You need to create the "conf" file to solve the 404 not found page. The conf file should be like this.

Open your terminal type the below commands

cd /etc/apache2/sites-available
ls
nano sample.conf

Add the below content in it.

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

Now you need to enable the sample.conf file by using the following command:

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

Then it will ask you to reload the Apache server, using

sudo service apache2 reload or restart

Then open your localhost/build folder and add the .htaccess file with the content of the below.

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*$ / [L,QSA]

Now the app is working normally.

Note: change the 0.0.0.0 IP address to your local IP address.


Will first solution work for "react-router-dom": "^5.1.2" ?, I am using <BrowserRouter>
.htaccess fixed it for me.
where, will i get the browserHistory, in solution 2.
adding only .htaccess file in the build folder fixed for me, thanks man
Why would a person sudo build?
S
Sebastian Kaczmarek

Add this to webpack.config.js:

devServer: {
    historyApiFallback: true
}

That's great for dev, but doesn't help with production builds.
Thank you... literally fixed it in a line
Works for me! 🔥
Why does it work? What is the explanation? What version(s) was it tested on?
P
Peter Mortensen

The Webpack Dev Server has an option to enable this. Open up package.json and add --history-api-fallback. This solution worked for me.

react-router-tutorial


It's fine with webpack-dev-server but how do I fix this for the production build?
P
Peter Mortensen

Production stack: React, React Router v4, BrowswerRouter, Express.js, Nginx

User BrowserRouter for pretty URLs File app.js import { BrowserRouter as Router } from 'react-router-dom' const App = () { render() { return ( // Your routes here ) } } Add index.html to all unknown requests by using /* File server.js app.get('/*', function(req, res) { res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) { if (err) { res.status(500).send(err) } }) }) bundle Webpack with webpack -p run nodemon server.js or node server.js

You may want to let nginx handle this in the server block and disregard step 2:

location / {
    try_files $uri /index.html;
}

getting path of undefined
@Goutham At the top of your server.js file add either import path from 'path or const path = require('path')
step 2 (catch all with /*) has just saved my day. Thank you! :)
This works, for people using redux and concerned with a connected router see this example: github.com/supasate/connected-react-router/tree/master/examples/…
P
Peter Mortensen

Try adding a ".htaccess" file inside the public folder with the below code.

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]

Thanks, this was what worked for me on Fedora 28 with apache. None of the above rewrite rules nor the ones on the CRA page worked for me. I added them to the virtual host setup instead of in a separate .htaccess file.
P
Peter Mortensen

If you are hosting using nginx and need a quick fix...

Add the following line to your nginx configuration inside the location block:

location / {
  try_files $uri /index.html;
}

P
Peter Mortensen

If you're using Firebase, all you have to do is make sure you've got a rewrites property in your firebase.json file in the root of your app (in the hosting section).

For example:

{
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]
  }
}

Further reading on the subject:

Configure rewrites

Firebase CLI: "Configure as a single-page app (rewrite all urls to /index.html)"


You are a legend
Thank you so much. Awesome solution!
P
Peter Mortensen

For those who are using IIS 10, this is what you should do to make this right.

Be sure that you are using browserHistory with this. As for reference, I will give the code for the routing, but this is not what matters. What matters is the next step after the component code below:

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

Since the problem is IIS receives requests from client browsers, it will interpret the URL as if it is asking for a page, then returns a 404 page since there isn't any available page. Do the following:

Open IIS Expand Server, and then open the Sites Folder Click the website/application Go to the Error Pages Open the 404 error status item in the list Instead of the option "Insert content from static file into the error response", change it to "Execute a URL on this site" and add "/" slash value to the URL.

And it will now work fine.

https://i.stack.imgur.com/sihHh.png

https://i.stack.imgur.com/12o6e.png


the best solution after 3-4 hours of searching. thanks a lot :)
M
Matt Goo

If you do have a fallback to your index.html, make sure that in your index.html file you have this:

<script>
  System.config({ baseURL: '/' });
</script>

This may differ from project to project.


Add this to your html head: <base href="/">
this worked for me, thanks (adding '' to the index.html head)
P
Peter Mortensen

If you are using Express.js or some other framework in the backend, you can add the similar configuration as below and check out the Webpack public path in the configuration. It should work fine even on reload if you are using BrowserRouter.

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

this is the simplest solution. note this route should go after any other routes as it's a catch all
changing from "" to "/" solved a similar problem for me. But rather than getting the error "cannot GET" as the OP, the browser received an empty HTML on refresh or URL-navigation.
P
Peter Mortensen

I found the solution for my SPA with React Router (Apache). Just add this in file .htaccess:

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

Source: Apache configuration for React Router


r
ravibagul91

Fixing the "cannot GET /URL" error on refresh or on calling the URL directly.

Configure your webpack.config.js to expect the given link the routes like this.

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },

This solution is the simplest and most efficient I have found here. Thank you very much!
P
Peter Mortensen

If you are using the "create-react-app" command,

to generate a React application then the package.json file needs to have one change for a properly running production build React SPA in a browser. Open up file package.json and add the following code segment to that,

"start": "webpack-dev-server --inline --content-base . --history-api-fallback"

Here the most important part is the "--history-api-fallback" to enable the history API call back.

Sometimes you will get a 404 error if you use Spring or any other back-end API. So in such a situation, you need to have a controller in the back-end to forward any request (you desired) to the index.html file to handle by react-router. The following demonstrates an example controller written using Spring.

@Controller
public class ForwardingController {
    @RequestMapping("/<any end point name>/{path:[^\\.]+}/**")
    public String forward(HttpServletRequest httpServletRequest) {
        return "forward:/";
    }
}

For example, if we take a back-end API REST endpoint as "abc" (http://localhost:8080/abc/**), any request coming to that endpoint will redirect to the React application (index.html file), and react-router will handle that afterwards.


You can also add multiple mappings like this: @RequestMapping({"/myurl1/**", "/myurl2/**"})
P
Peter Mortensen

Using HashRouter worked for me with Redux also. Just simply replace:

import {
    Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
        <Provider store={Store}>
            <Router history={history}> // Replace here saying Router
                <Layout/>
            </Router>
        </Provider>
    </LocaleProvider>, document.getElementById("app"));

registerServiceWorker();

with:

import {
    HashRouter // Replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
        <Provider store={Store}>
            <HashRouter history={history}> //replaced with HashRouter
                <Layout/>
            </HashRouter>
        </Provider>
    </LocaleProvider>, document.getElementById("app"));

registerServiceWorker();

The syntax highlighting of "ReactDOM" is just weird.
P
Peter Mortensen

Here is a simple, clear and better solution. It works if you use a web server.

Each web server has an ability to redirect the user to an error page in case of HTTP 404. To solve this issue, you need to redirect the user to the index page.

If you use a Java base server (Tomcat or any Java application server), the solution could be the following:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

Example:

GET http://example.com/about

The web server throws HTTP 404 because this page does not exist on the server side

the error page configuration tells to the server that send the index.jsp page back to the user

then JavaScript will do the rest of the job on the client side, because the URL on the client side is still http://example.com/about.

That is it. No more magic needs:)


This was awesome! I am using Wildfly 10.1 server and made this update to my web.xml file, except I had location set to just '/'. This was because in my react code I used browser history like this: const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Are you sure that this doesn't impact SEO? You are giving a 404 status to pages that actually exist. The user might never realise this, but bots do pay a lot of attention, in fact so much, that they will not scrape your page.
P
Peter Mortensen

I solved this problem by changing file webpack.config.js.

My new configuration looks like:

Before

output: {
  path: path.join(__dirname, '/build/static/js'),
  filename: 'index.js'
},


devServer: {
  port: 3000
}

After

output: {
  path: path.join(__dirname, '/build/static/js'),
  filename: 'index.js',
  publicPath: '/'
},


devServer: {
  historyApiFallback: true,
  port: 3000
}

P
Peter Mortensen

I'm not using server-side rendering yet, but I hit the same problem as the OP where Link seemed to work fine most of the time, but failed when I had a parameter. I'll document my solution here to see if it helps anyone.

My main JSX content contains this:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

This works fine for the first matching link, but when the :id changes in <Link> expressions nested on that model's detail page, the URL changes in the browser bar, but the content of the page did not initially change to reflect the linked model.

The trouble was that I had used the props.params.id to set the model in componentDidMount. The component is just mounted once, so this means that the first model is the one that sticks on the page and the subsequent Links change the props, but leave the page looking unchanged.

Setting the model in the component state in both componentDidMount and in componentWillReceiveProps (where it is based on the next props) solves the problem and the page content changes to reflect the desired model.


It might be better to use the component constructor (which also has access to props) i.s.o. componentDidMount if you ever want to try for server-side rendering. Because componentDidMount is only called in the browser. It's purpose is to do stuff with the DOM, such as attaching event listeners to body etc that you can't do in render.
A
Agney

Adding more information to Joshua Dyck's answer.

If you are using Firebase and want to use both the root route and a sub-directory route you need to add the following code in your firebase.json:

{
  "hosting": {
    "rewrites": [
      {
        "source": "*",
        "destination": "/index.html"
      },
      {
        "source": "/subdirectory/**",
        "destination": "/subdirectory/index.html"
      }
    ]
  }
}

Example:

You are building a website for a client. You want the owner of the website to add information in https://your.domain.com/management while the users of the website will navigate to https://your.domain.com.

In this case your firebase.json file will look like that:

{
  "hosting": {
    "rewrites": [
      {
        "source": "*",
        "destination": "/index.html"
      },
      {
        "source": "/management/**",
        "destination": "/management/index.html"
      }
    ]
  }
}

D
Dušan

I am using ASP.NET Core and React. The solution for the problem of manual routing and refreshing routes in production environment was to create web.config file in the root of the main project of ASP.NET Core which will configure routing on the production server.

Location of the file inside a project:

https://i.stack.imgur.com/SBc06.png

Content of the web.config file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Rewrite Text Requests" stopProcessing="true">
                    <match url=".*" />
                    <conditions>
                        <add input="{HTTP_METHOD}" pattern="^GET$" />
                        <add input="{HTTP_ACCEPT}" pattern="^text/html" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/index.html" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

P
Peter Mortensen

If you are hosting in IIS: Adding this to my webconfig solved my problem

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

You can make a similar configuration for any other server.