How to use react-router-dom
Create a protected route and store the response in localStorage so that the user can view its details again the next time it is opened. After logging in, you should be redirected to the dashboard page.
All functions are added in ContextApi.
Codesandbox link: Code
I tried, but couldn't achieve it.
Routing page
import React, { useContext } from "react"; import { globalC } from "./context"; import { Route, Switch, BrowserRouter } from "react-router-dom"; import About from "./About"; import Dashboard from "./Dashboard"; import Login from "./Login"; import PageNotFound from "./PageNotFound"; function Routes() { const { authLogin } = useContext(globalC); console.log("authLogin", authLogin); return ( <BrowserRouter> <Switch> {authLogin ? ( <> <Route path="/dashboard" component={Dashboard} exact /> <Route exact path="/About" component={About} /> </> ) : ( <Route path="/" component={Login} exact /> )} <Route component={PageNotFound} /> </Switch> </BrowserRouter> ); } export default Routes;
Context page
import React, { Component, createContext } from "react"; import axios from "axios"; export const globalC = createContext(); export class Gprov extends Component { state = { authLogin: null, authLoginerror: null }; componentDidMount() { var localData = JSON.parse(localStorage.getItem("loginDetail")); if (localData) { this.setState({ authLogin: localData }); } } loginData = async () => { let payload = { token: "ctz43XoULrgv_0p1pvq7tA", data: { name: "nameFirst", email: "internetEmail", phone: "phoneHome", _repeat: 300 } }; await axios .post(`https://app.fakejson.com/q`, payload) .then((res) => { if (res.status === 200) { this.setState({ authLogin: res.data }); localStorage.setItem("loginDetail", JSON.stringify(res.data)); } }) .catch((err) => this.setState({ authLoginerror: err }) ); }; render() { // console.log(localStorage.getItem("loginDetail")); return ( <globalC.Provider value={{ ...this.state, loginData: this.loginData }} > {this.props.children} </globalC.Provider> ); } }
P粉1229324662023-09-22 12:07:30
import { Routes, Route, Navigate } from "react-router-dom"; function App() { return ( <Routes> <Route path="/public" element={<PublicPage />} /> <Route path="/protected" element={ <RequireAuth redirectTo="/login"> <ProtectedPage /> </RequireAuth> } /> </Routes> ); } function RequireAuth({ children, redirectTo }) { let isAuthenticated = getAuth(); return isAuthenticated ? children : <Navigate to={redirectTo} />; }
Link to documentation: https://gist.github.com/mjackson/d54b40a094277b7afdd6b81f51a0393f
P粉5877801032023-09-22 00:11:42
<BrowserRouter> <Switch> {authLogin ? ( <> <Route path="/dashboard" component={Dashboard} exact /> <Route exact path="/About" component={About} /> </> ) : ( <Route path="/" component={Login} exact /> )} <Route component={PageNotFound} /> </Switch> </BrowserRouter>
Switch
does not handle any rendering except for Route
and Redirect
components. If you wanted to "nest" like this, then you would need to wrap each component in a common route, but this is completely unnecessary.
Your login component also does not handle redirecting back to the "homepage" or private route originally visited.
react-router-dom
v6In version 6, custom routing components are no longer popular, and the recommended method is to use the authentication layout component.
import { Navigate, Outlet } from 'react-router-dom'; const PrivateRoutes = () => { const location = useLocation(); const { authLogin } = useContext(globalC); if (authLogin === undefined) { return null; // or loading indicator/spinner/etc } return authLogin ? <Outlet /> : <Navigate to="/login" replace state={{ from: location }} />; }
...
<BrowserRouter> <Routes> <Route path="/" element={<PrivateRoutes />} > <Route path="dashboard" element={<Dashboard />} /> <Route path="about" element={<About />} /> </Route> <Route path="/login" element={<Login />} /> <Route path="*" element={<PageNotFound />} /> </Routes> </BrowserRouter>
or
const routes = [ { path: "/", element: <PrivateRoutes />, children: [ { path: "dashboard", element: <Dashboard />, }, { path: "about", element: <About /> }, ], }, { path: "/login", element: <Login />, }, { path: "*", element: <PageNotFound /> }, ];
...
export default function Login() { const location = useLocation(); const navigate = useNavigate(); const { authLogin, loginData } = useContext(globalC); useEffect(() => { if (authLogin) { const { from } = location.state || { from: { pathname: "/" } }; navigate(from, { replace: true }); } }, [authLogin, location, navigate]); return ( <div style={{ height: "100vh" }} className="d-flex justify-content-center align-items-center" > <button type="button" onClick={loginData} className="btn btn-primary"> Login </button> </div> ); }
react-router-dom
v5Create a PrivateRoute
component that consumes your authentication context.
const PrivateRoute = (props) => { const location = useLocation(); const { authLogin } = useContext(globalC); if (authLogin === undefined) { return null; // or loading indicator/spinner/etc } return authLogin ? ( <Route {...props} /> ) : ( <Redirect to={{ pathname: "/login", state: { from: location } }} /> ); };
Update your Login
component to handle redirects back to the original access route.
export default function Login() { const location = useLocation(); const history = useHistory(); const { authLogin, loginData } = useContext(globalC); useEffect(() => { if (authLogin) { const { from } = location.state || { from: { pathname: "/" } }; history.replace(from); } }, [authLogin, history, location]); return ( <div style={{ height: "100vh" }} className="d-flex justify-content-center align-items-center" > <button type="button" onClick={loginData} className="btn btn-primary"> Login </button> </div> ); }
Render all routes in a "flat list"
function Routes() { return ( <BrowserRouter> <Switch> <PrivateRoute path="/dashboard" component={Dashboard} /> <PrivateRoute path="/About" component={About} /> <Route path="/login" component={Login} /> <Route component={PageNotFound} /> </Switch> </BrowserRouter> ); }