简体   繁体   中英

PrivateRoute in React-router-dom v6 isnt working

I'm trying to use ProtectedRoute as I can't see why the code isn't working, I'm not getting any error, but at /account it should display <Profile/> and it's blank, I can see the header and footer, but the whole <Profile/> is missing before trying to use a PrivateRoute , I could display Profile with any problem.

my ProtectedRoute.js

 import React from "react";
    import { useSelector } from "react-redux";
    import { Navigate, Outlet } from "react-router-dom";

    const ProtectedRoute = () => {
    const {isAuthenticated} = useSelector((state)=>state.user)
    return isAuthenticated ? <Outlet /> : <Navigate to="/login"/>
    }

    export default ProtectedRoute;

my app.js

    function App() {

    const {isAuthenticated, user} = useSelector(state=>state.user)
    React.useEffect(() => {

    WebFont.load({
      google:{
        families: [ "Droid Sans", "Chilanka"],
      },
    });

    store.dispatch(loadUser())
    }, []);
  
    return (
    <Router>
      <Header/>
      {isAuthenticated && <UserOptions user={user} />}
      <Routes>
        <Route exact path="/" element={<Home/>}/>
        <Route exact path="/product/:id" element={<ProductDetails/>}/>
        <Route exact path="/products" element={<Products/>}/>
        <Route path="/products/:keyword" element={<Products/>}/>
        <Route exact path="/search" element={<Search/>}/>
        <Route exact path="/account" element={<ProtectedRoute/>}/>
          <Route exact path="/account" element={<Profile/>}/>
        <Route exact path="/login" element={<LoginSignUp/>}/>
      </Routes>
    <Footer/>
    </Router>
    );
   }

   export default App;

and my Profile

const Profile = () => {
 const { user, loading, isAuthenticated} = useSelector((state) => state.user);

 const navigate = useNavigate();

useEffect(() => {
    if(isAuthenticated === false){
        navigate("/login");
    }
}, [navigate,isAuthenticated])
return (
        <Fragment>
          <MetaData title={`${user.name}'s Profile`} />
          <div className="profileContainer">
            <div>
              <h1>My Profile</h1>
              <img src={user.avatar?.url} alt={user.name} />
              <Link to="/me/update">Edit Profile</Link>
            </div>
            <div>
              <div>
                <h4>Full Name</h4>
                <p>{user.name}</p>
              </div>
              <div>
                <h4>Email</h4>
                <p>{user.email}</p>
              </div>
              <div>
                <h4>Joined On</h4>
                <p>{String(user.createdAt).substr(0, 10)}</p>
              </div>                
              <div>
                <Link to="/orders">My Orders</Link>
                <Link to="/password/update">Change Password</Link>
              </div>
            </div>
          </div>
        </Fragment>
  );
};

export default Profile;

You are rendering two routes for the same "/account" path. ProtectedRoute is rendered on its own self-closing route, so the second route rendering Profile is unreachable.

<Routes>
  <Route exact path="/" element={<Home/>}/>
  <Route exact path="/product/:id" element={<ProductDetails/>}/>
  <Route exact path="/products" element={<Products/>}/>
  <Route path="/products/:keyword" element={<Products/>}/>
  <Route exact path="/search" element={<Search/>}/>
  <Route exact path="/account" element={<ProtectedRoute/>}/>
  <Route exact path="/account" element={<Profile/>}/> // <-- unreachable, oops!
  <Route exact path="/login" element={<LoginSignUp/>}/>
</Routes>

Remove the path prop from the layout route rendering the ProtectedRoute and ensure it is actually wrapping other Route components. You may as well also remove the exact prop on all the routes as this prop was removed in RRDv6.

Example:

<Router>
  <Header/>
  {isAuthenticated && <UserOptions user={user} />}
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/product/:id" element={<ProductDetails />} />
    <Route path="/products" element={<Products />} />
    <Route path="/products/:keyword" element={<Products />} />
    <Route path="/search" element={<Search />} />
    <Route element={<ProtectedRoute />}>
      <Route path="/account" element={<Profile />} /> // <-- wrapped by layout route!
    </Route>
    <Route path="/login" element={<LoginSignUp />} />
  </Routes>
  <Footer/>
</Router>

The ProtectedRoute component doesn't appear to wait for the user state to populate before rendering either the Outlet for protected content or the redirect. Apply some conditional rendering to render a loading indicator or similar while the user state is populated.

import React from "react";
import { useSelector } from "react-redux";
import { Navigate, Outlet } from "react-router-dom";

const ProtectedRoute = () => {
  const user = useSelector((state) => state.user);

  if (!user) return null; // <-- or loading indicator, etc...

  return user.isAuthenticated
    ? <Outlet />
    : <Navigate to="/login" replace />;
}

export default ProtectedRoute;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM