Reputation:
I have a login page
which has a forgot password
link and it takes the user to forgot password page.
When I click on the forgot password
link, it changes the URL but does not load the component.
Code for login page
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
// assets
import Logo from "../../../assets/images/kvh-logo.svg";
import bgImgArray from "../../../assets/images/bg";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
import "./Login.css";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const userid = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
// for changing backgrounds
const [index, setIndex] = useState(0);
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...userid}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
type="password"
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<Button
className="pull-right"
block="true"
type="submit"
bssize="small"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</Button>
<br />
</div>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
In routing code, I have Admin Layout which looks after the dashboard and AuthLayout which looks after the Login page.
I tried searching for the solution but unfortunately couldn't find any solutions. Hence, posting it here.
Router Code
import React from "react";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
import { createBrowserHistory } from "history";
import AdminLayout from "layouts/Admin/Admin.js";
import AuthLayout from "layouts/Auth/Auth.js";
import ResetPassword from "../components/pages/reset-password/ResetPassword";
const hist = createBrowserHistory();
const AppRouter = () => {
return (
<Router history={hist}>
<Switch>
<Route path="/admin" render={(props) => <AdminLayout {...props} />} />
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
<Redirect from="/" to="/auth" />
</Switch>
</Router>
);
};
export default AppRouter;
Adding Auth Layout Code
import React from "react";
import { Route, Switch, Redirect, Link } from "react-router-dom";
import Login from "../../components/pages/login/Login";
import ResetPassword from "../../components/pages/reset-password/ResetPassword";
import routes from "routes/routes.js";
class Pages extends React.Component {
getRoutes = (routes) => {
return routes.map((prop, key) => {
if (prop.collapse) {
return this.getRoutes(prop.views);
}
if (prop.layout === "/auth") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
} else {
return null;
}
});
};
getActiveRoute = (routes) => {
let activeRoute = "WATCH";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = this.getActiveRoute(routes[i].views);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else {
if (
window.location.pathname.indexOf(
routes[i].layout + routes[i].path
) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
componentDidMount() {
document.documentElement.classList.remove("nav-open");
}
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
<Switch>
{this.getRoutes(routes)}
<Redirect from="*" to="/auth/login" />
</Switch>
</div>
</div>
);
}
}
export default Pages;
Upvotes: 1
Views: 1135
Reputation: 202836
The issue is in the way you've specified the order of the routes. The Switch
component "Renders the first child <Route>
or <Redirect>
that matches the location. The path "/"
rendering the AuthLayout
component will be matched by any URL path, the "/forgotPassword"
route is unreachable.
Within the Switch
component you should order the routes in inverse order of path specificity. In other words, the more specific paths should be rendered prior to less specific paths. The idea is that if any given path doesn't match the URL path it "falls-through" to the less and less specific route paths until eventually it hits either a "/"
or generic "catch-all" route.
There's an additional issue you'll hit after reordering the routes with the "/"
route which will block the "catch-all" Redirect
. For this one route you'll need to use the exact
prop so only exactly "/"
will match, and then any other path that wasn't already matched above will be safely handled by the redirect. Of course, for this redirect to work you actually need to render a route for path="/auth"
somewhere. Below I've simply mapped it also to the AuthLayout
route component.
Example suggestion:
<Switch>
<Route path="/admin" component={AdminLayout} />
<Route path="/forgotPassword" component={ResetPassword} />
<Route path={["/auth", "/"]} component={AuthLayout} />
<Redirect to="/auth" />
</Switch>
Upvotes: 0
Reputation: 99
Recently I faced this issue with React 18. Version of react was 18.0.1 and react-router-dom was 5.1.0 so StrictMode is causing the issue Just remove or comment the Tag <React.StrictMode> and it should work fine.
Upvotes: 0
Reputation: 795
look at this code that you wrote:
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
it's never going to /forgotPassword
because path always match with first Route.
you should use exact
props:
<Route exact path="/" render={(props) => <AuthLayout {...props} />} />
<Route exact path="/forgotPassword" component={ResetPassword} />
Upvotes: 0
Reputation: 2309
Wrap inside Router as given below.
import {
BrowserRouter as Router,
} from "react-router-dom";
<Router><Link to="/forgotPassword">Forgot password?</Link> <Router/>
Upvotes: 0
Reputation: 17608
If you create your own history
then you should use Router
instead of BrowserRouter
.
import {
Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
Because, BrowserRouter
comes with its own history
and you provide another one, this causes the problem.
Upvotes: 0