I have a problem with implementing fully functional 404 page with React HashRouter.
routes.tsx
export const Routes = () => (
<HashRouter>
<Switch>
<Route path="/" exact={true} component={Home} />
<Route path="/route1" component={Route1} />
<Route path="/route2" component={Route2} />
<Route component={ErrorPage} />
</Switch>
</HashRouter>
);
app.tsx
<>
...
<Routes />
...
</>
Current behaviour:
When typing in address bar {url}/#/{non-existing-route}, then behaviour is correct - browser displays ErrorPage
When typing in address bar {url}/{non-existing-route}, then behaviour is incorrect - browser changes url to {url}/{non-existing-route}#/ and displays Home page.
I would like ErrorPage to be displayed in both situations.
This problem does not occur when I am using BrowserRouter, but I must use HashRouter here.
Thanks for your help in advance!
Related
When I send the user to a URL (in this case /login and /signup and then back to /)
the elements on my page don't load automatically by entering the URL, I instead have to manually refresh in order for the content to load.
This is my index.js which handles the routes (with react router v6)
import Navbar from "./components/navbar/navbar";
import Footer from "./components/footer/footer";
import Home from "./pages/home/home";
import Login from "./pages/user/login/login";
import Signup from "./pages/user/signup/signup";
ReactDOM.render(
<div>
<Navbar />
<BrowserRouter>
<Routes>
<Route index path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
</BrowserRouter>
<Footer />
</div>,
document.getElementById("root")
);
This is an example of what happens when I send the user to /login with the use of the Link component that I am importing through React-router.
Before refreshing the URL on /login
I then have to manually reload the page by pressing the reload button, only after doing so the content will load.
After refreshing the URL on /login
the same applies if I try to go back to the homepage using the Link component
Before refreshing on the URL /
I then have to reload in order to remove the login box
After refreshing on the URL /
I reproduced the same behavior. CodeSandbox (try it and look at home.js)
You don't seem to be using the page navigation correctly. The most react way is to use the useNavigation() hook.
If navigation is working but the routes aren't then I suspect you are rendering them into separate routers. I notice you render the Navbar outside your BrowserRouter, and since navigation is working and updating the URL in the address bar is fairly safe to assume those links are correctly wrapped in a router. You'd see an invariant warning/error otherwise.
ReactDOM.render(
<div>
<Navbar /> // <-- must have another router if it is working
<BrowserRouter>
<Routes>
<Route index path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
</BrowserRouter>
<Footer />
</div>,
document.getElementById("root")
);
You should have only one router wrapping the app and providing the routing context to all Routes, Route, Navigate, Link, etc... components.
Remove any extraneous routers in other components, like NavBar, and move them all to be within the BrowserRouter you are rendering here.
ReactDOM.render(
<BrowserRouter>
<Navbar />
<Routes>
<Route index path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
<Footer />
</BrowserRouter>,
document.getElementById("root")
);
I am designing a users dashboard in React, wherein User logs in and navigate to his dashboard with other links such as Archived, Profile and Settings. My Login and then Navigating to HomePage is working fine.
I have designed the Dashboard, Archived, Profile and Settings Page Links in Sidebar. Now when I am navigating to the Links. It takes me to a new URL path and my Sidebar disappears. I want my sidebar to still appear on all pages as long as I am logged in.
Below is my App.js where I have designed the upper level route:
return (
<div>
<Router history={history}>
<Switch>
<PrivateRoute exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<Redirect from="*" to="/" />
</Switch>
</Router>
</div>
);
Once User is on HomePage, I need the dashboard and other links to show. and when User clicks on any Sidebar link, Sidebar should still be there while the other page opens. So I added the inner Routes to the HomePage.jsx like this:
return (
<div>
<Router history={history}>
<Sidebar />
<Switch>
<Route path="/" component={Dashboard} />
<Route path="/archived" component={ArchivedPage} />
<Route path="/profile" component={ProfilePage} />
<Route path="/settings" component={SettingsPage} />
{/* <Redirect from="*" to="/" /> */}
</Switch>
</Router>
</div>
);
But it doesn't work. Can anyone please help me understanding if this is correct. or how can I achieve the required result.
Please let me know if you need any other details.
Thank you.
Issue
The issue is that you are exactly matching "/" to render HomePage, but then as soon as the path is any deeper, like "/archived", it no longer exactly matches and HomePage is unmounted.
You should not render a Router within another Router, you need only one router per app to provide the routing context.
Solution
Remove the inner router (and any other nested routers!!). Remove the exact prop on the "/" path and reorder the routes to specify the more specific paths before less specific paths so the Switch can actually do its job and match and render the appropriate route.
App
Reorder the more specific "/login" and "/register" paths to be matched prior to the less specific "/" path. If you had, for example, a "/login/google" path, this is more specific than "/login" and would be ordered earlier.
return (
<div>
<Router history={history}>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<PrivateRoute path="/" component={HomePage} />
<Redirect to="/" />
</Switch>
</Router>
</div>
);
HomePage
Move the Dashboard to the last route position so if no other route above it is matched and returned then the dashboard is matched and rendered.
return (
<div>
<Sidebar />
<Switch>
<Route path="/archived" component={ArchivedPage} />
<Route path="/profile" component={ProfilePage} />
<Route path="/settings" component={SettingsPage} />
<Route component={Dashboard} />
</Switch>
</div>
);
in first place your doing something wrong you cant put a Router inside a Router,
you haver a Router in app then inside a component that is inside of app you have another Router thats a problem i dont know if that solves your problem but try it just delete Router in homepage
I'm in the process of trying to figure out how React Router Dom works, and have run into a problem that I am not even sure how to figure out.
If, within App.js, I have this snippet, then everything renders and navigates between the sections, just fine.
<Switch>
<Route path='/login-user' component={UserLoginForm} />
<Route path='/login-database' component={LoginDatabaseSelect} />
<Route path='/login-project' component={LoginProjectSelect} />
<Redirect path='/' exact to='/login-user' />
</Switch>
However, that is not the proper way, and I am trying to instead do this:
Create a different component that is referenced for this.
<Switch>
<Route path='/login' component={LoginSequence} />
<Redirect path='/' exact to='/login' />
</Switch>
And in LoginSequence the code is:
import React from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import UserLoginForm from "./LoginSequence/loginUserPass";
import LoginDatabaseSelect from "./LoginSequence/loginDatabaseSelect";
import LoginProjectSelect from "./LoginSequence/loginProjectSelect";
class LoginSequence extends React.Component {
render () {
console.log('Login Sequence')
return (
<div>
<h1>Login Sequence</h1>
<Route path='/login-user' component={UserLoginForm} />
<Route path='/login-database' component={LoginDatabaseSelect} />
<Route path='/login-project' component={LoginProjectSelect} />
<Redirect path='/login' exact to='/login-database' />
</div>
)
}
}
export default LoginSequence;
however, this component, does not even seem to be called as the console.log does not output anything to console. Meaning that nothing is rendering, and it is not getting called.
Can someone point me in the right direction as to why this isn't working?
Edit: I fixed up the From and changed it to Path in the snippet of code where LoginSequence is called, since it had no effect, but path seems to be the normal way of doing it.
Change from to path in Redirect.
<Switch>
<Route path='/login' component={LoginSequence} />
<Redirect path='/' exact to='/login' />
</Switch>
Don't use Redirect use Route and call in the component you want to render
<Switch>
<Route path='/login-user' component={UserLoginForm} />
<Route path='/login-database' component={LoginDatabaseSelect} />
<Route path='/login-project' component={LoginProjectSelect} />
<Route path='/' exact component={login-user} />
</Switch>
After talking to a couple of people, and seeing a few more examples, something became a bit more clear that solved the issue.
Within App.js:
<Switch>
<Route path='/login' component={LoginSequence} />
<Redirect path='/' exact to='/login' />
</Switch>
And the LoginSequence component within LoginScreen.js:
class LoginSequence extends React.Component {
render () {
console.log('Login Sequence')
return (
<div>
<h1>Login Sequence</h1>
<Route path='/login/user' component={UserLoginForm} />
<Route path='/login/database' component={LoginDatabaseSelect} />
<Route path='/login/project' component={LoginProjectSelect} />
<Redirect path='/login' exact to='/login/user' />
</div>
)
}
}
What I noticed, is that the 'root' for referencing changes every time, so, once I redirect to /login to be loginsequence, the new area that components are looked for is within /login. Thus /login/<user/database/project> has to be the new path, rather than just /<user/database/project>, as it seems to look for the references of that, within App.js and can't find them, thus not rendering. Seems like RRD follows a 'tree' structure of a sort, and every time you want to nest it, you have to follow the structure of
/<path of component containing further routes>/<route of component referenced in component>
Local runs fine, but when run on "npm run deploy" the website returns 404.
I have a React + TypeScript app which utilises react-router-dom BrowserRouter to navigate between pages.
I am ware of the issue on github pages with react-router, therefore I have tried adding basename={process.env.PUBLIC_URL}, changing it to a HashRouter, and many many more possibilities.
I've been on this issue for 7 hours straight... and there seems to be no resources on this particular problem for Typescript.
Could someone please help me!
index.tsx
ReactDOM.render(
<AppRouter />,
document.getElementById('root') as HTMLElement
);
Router.tsx
export const AppRouter: React.StatelessComponent<{}> = () => {
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<div>
<NavBar />
<Switch>
<Route exact={true} path='/' component={App} />
<Route exact={true} path='/' component={Notes} />
<Route exact={true} path='/About' component={About} />
</Switch>
</div>
</BrowserRouter>
);
Also tried Router.tsx
export const AppRouter: React.StatelessComponent<{}> = () => {
return (
<HashRouter>
<div>
<NavBar />
<Switch>
<Route exact={true} path='/' component={App} />
<Route exact={true} path='/' component={Notes} />
<Route exact={true} path='/About' component={About} />
</Switch>
</div>
</HashRouter>
);
NavBar.tsx
export const NavBar: React.StatelessComponent<{}> = () => {
return (
<div id='nav-bar'>
<AppBar position='static' color='default'>
<Toolbar>
<span id='navbar-title'>
<Typography variant='title' color='default'>
Keep
</Typography>
</span>
<Link to="/">
<Button size="large" color="primary" variant="text">Notes</Button>
</Link>
<Link to="/About">
<Button size="large" color="primary" variant="text">About</Button>
</Link>
</Toolbar>
</AppBar>
</div>
)
Thank you for reading.
EDIT below is the exact error response from web console at 404 error. Something about favicon, could that be an issue? The location is completely wrong
json469.github.io/:1 Refused to load the image 'https://json469.github.io/favicon.ico' because it violates the following Content Security Policy directive: "img-src data:".
I've also tried debugging by printing out the process.env.PUBLIC_URL, however it returned an empty string...
EDIT2 below is the change made to Router.tsx that fixed the issue.
<HashRouter>
<div>
<NavBar />
<main>
<Switch>
<Route exact={true} path='/' component={App} />
<Route exact={true} path='/' component={Notes} />
<Route exact={true} path='/About' component={About} />
</Switch>
</main>
</div>
</HashRouter>
This is unlikely to be due to TypeScript - or even React Router. If you receive a 404, that is because your server has to redirect the user to your React app if they visit a route that the server does not know, and your app can then pretend to do actual routing.
However, it appears that GitHub Pages does not support this. Thus, you cannot do "proper" URLs like username.github.io/page/some-route. A workaround is to use the Hash Router, as you mentioned, but that means that URLs will look like username.github.io/page#/some-route.
Thus, your issue is probably that you're trying the former route, rather than the latter. So to solve it, you can either try that kind of route, or move to a different host.
I have 2 level of route, the first layer it look like this
<BrowserRouter>
<div>
<AuthRoute></AuthRoute>
<Switch>
<Route path='/dashboard' component={Dashboard} />
</Switch>
</div>
</BrowserRouter>
where in AuthRoute I have a redirect upon componentDidMount
this.props.history.replace(/dashboard/redirected)
The problem is within the dashboard/index which is my 2nd level of route config
<BrowserRouter>
<Switch>
<Route exact path='/dashboard' component={()=><h1>dashboard index</h1>} />
<Route exact path='/dashboard/somewhere' component={()=><h1>somewhere</h1>} />
<Route exact path='/dashboard/redirected' component={() => <h1>redirected</h1>} />
</Switch>
</BrowserRouter>
The route changed to /dashboard/redirect but the component didn't render the correct one if you refresh on says /dashboard/somewhere or /dashboard/
You can see the problem clearer in the demo I setup https://codesandbox.io/s/v0v4qok38l
You only need one <BrowserRouter> in the application - removing it from dashboard/index.js will fix your issue.