15
votes

This is my first time using ReactJS and is not my project however, I am trying to redirect any non-existent routes to a 404 page that I have made. My 404 page is currently displaying as intended when any URL that doesn't match a route is entered apart from when the URL contains /#/.

For example, this URL will redirect to my 404 page:

http://localhost:8080/non-existent-url

But this URL will only load my app's default route (homepage):

http://localhost:8080/#/non-existent-url

I don't know what the /#/ is for and it appears that the app will display pages with valid routes with or without it.

Stripped down routes file:

import React from "react";
import { Router, Route, IndexRoute, browserHistory, hashHistory, Redirect } from "react-router/es";
import willTransitionTo from "./routerTransition";
import App from "./App";
import DashboardContainer from "./components/Dashboard/DashboardContainer";
import DashboardAccountsOnly from "./components/Dashboard/DashboardAccountsOnly";
import NotFound from './components/NotFound';

const history = __HASH_HISTORY__ ? hashHistory : browserHistory;

class Auth extends React.Component {
    render() {return null; }
}

const routes = (
    <Route path="/" component={App} onEnter={willTransitionTo}>
        <IndexRoute component={DashboardContainer}/>
        <Route path="/auth/:data" component={Auth}/>
        <Route path="/dashboard" component={DashboardContainer}/>

        <Route path='*' exact={true} component={NotFound} />
    </Route>
);

export default class Routes extends React.Component {
    render() {
        return <Router history={history} routes={routes} />;
    }
}

404 (NotFound) component:

import React from 'react';
import {Link} from "react-router/es";
import Translate from "react-translate-component";

var image = require("assets/404.png");

const NotFound = () =>

    <div className="grid-block main-content wrap regular-padding">
        <div className="grid-content small-12" style={{marginTop:"200px"}}>
            <div style={{ textAlign:"center"}}>
                <img style={{marginBottom:"1.2rem"}} src={image} />
                <h3>404 page not found</h3>
                <p>This page does not exist.</p>
                <Link className="button" to={"/dashboard"}>
                    <Translate style={{color:"#fff"}} content="account.home" />
                </Link>
            </div>
        </div>
    </div>

export default NotFound;

How can I redirect users to my 404 page when a URL doesn't match any route and when the URL contains /#/?

7
Which version of React Router are you using?MattD
You may want to read the difference between <BrowserRouter> and <HashRouter> on the React Router docs. reacttraining.com/react-router/web/api/HashRouterphilipjc
By the looks of it all you need to do is to use browserHistory instead of hashHistory like const history = browserHistory;Shubham Khatri
@MattD I am using react-router v3.0.2. I tried Shubham's suggestion and it made no difference.dom_ahdigital
Please check here. It will help you.Japar Sathik

7 Answers

7
votes

For react-router < 4.0

If you want to display an error page and not change the path

<Route path='*' exact={true} component={errorcomponent} />

If you want to display an error page and change the path

<Route path='/error' component={errorcomponent} />
 // use redirect to  change the Url
<Redirect from='*' to='/error' />

For react-router 4

Keep the path

<Switch>
    <Route exact path="/" component={component} />
    <Route component={errorcomponent} />
 </Switch>

change Url.

 <Switch>
    <Route path="/comp" component={component} />
   <Redirect to="/error" />
 </Switch>

the redirect tag has to be put whereever you place a route tag insde a switch.

2
votes

Based on what I'm seeing in your code, trying to switch between hash history and browser history just isn't going to work. The moment you hit '/#' in anything it's going to break and always hit your generic '/' route.

Hash URLs are an older way of handling things that shouldn't be an issue anymore. See this SO question and its accepted answer for more details.

Doing links like Twitter, Hash-Bang #! URL's

So yeah, unless you have some crazy reason to still support hashes in URLs, drop hash history and simply use browser history. Then your routes as defined in your provided code sample should work.

2
votes

You can access the location.hash value and set it to blank.

But as already well described, this is standard http for years and you shouldn't care except for a good reason.

You can see it like http://localhost:8080/?non-existent-url it means go to / with path parameter non-existent-url do you want to redirect too ?

2
votes

There are few unsafe characters which should be avoided while creating URL one of them is #.

The character “#” is unsafe and should always be encoded because it is used in World Wide Web and in other systems to delimit a URL from a fragment/anchor identifier that might follow it.

Server while resolving the route will pass the string before # to get the page requested and then it will be moved the exact fragment of page if anything specifed using the string after # till any unsafe character.

So, In your string before # is empty that's why it redirect to / which is a valid route and it redirect to component App. And since no fragment is defined that's why nothing happening.

To make this works we need to tell nginx or any other server to strip down all the # before sending request forward.

In that case,

http://localhost:8080/#/non-existent-url

will be converted to,

http://localhost:8080/non-existent-url

Which will solve the issue. See this link to see How to rewrite rule in nginx

1
votes

You can use this

<Route exact insecure component={NotFound} />
1
votes

The problem is that in the URL http://localhost:8080/#/non-existent-url, the route is just /, which is why it is hitting your app component. The intent of the # in a standard URL means go to a location within a page. So your url is saying "go to the / page and go to this specific location once you've loaded the page".

From a routing perspective, / is your route, so it is found. Honestly, if you aren't doing the 5 year old hash-bang style of routing, this is behaving as intended. In that case (if you are stuck with people bookmarked to those kinds of pages or something) to force something like that to be "not found", I would institute URL re-writing.

SPA apps already rewrite incoming URLs on the server so they are all directed at a single page. Add a rule that as part of the rewrite, it removes the # (if you don't use it in your app).

So, http://localhost:8080/#/non-existent-url would internally be processed by the app as http://localhost:8080/non-existent-url.

1
votes

you should Router component as parent of your Routes and add this Route

<Route path='*' component={() => NotFound} />

const routes = (
    <Router>
        <div>
            <Route path="/" component={App} onEnter={willTransitionTo}
            <IndexRoute component={DashboardContainer}/>
            <Route path="/auth/:data" component={Auth}/>
            <Route path="/dashboard" component={DashboardContainer}/>

            <Route path='*' component={() => NotFound} />
        </div>
    </Router>
);