Home > other >  Uncaught Error: [ProtectedRoute] is not a <Route> component. All component children of <Rou
Uncaught Error: [ProtectedRoute] is not a <Route> component. All component children of <Rou

Time:07-29

I am using react-router-dom v6.3.0 .. i am getting Uncaught Error: [ProtectedRoute] is not a component. All component children of must be a or <React.Fragment> .. i tried many ways but also getting same error ... please help me to solve this error

this is App.js file

import "./App.css";
import { useEffect} from "react"; 
import { BrowserRouter as Router,Route,Routes} from "react-router-dom";
import Header  from "./component/layout/Header/Header";
import WebFont from "webfontloader";
import React from "react";
import { useSelector } from "react-redux";
import Footer from "./component/layout/Footer/Footer.js"
import Home from "./component/Home/Home.js"
import ProductDetails from "./component/Product/ProductDetails.js"
import Products from './component/Product/Products.js'
import Search from './component/Product/Search.js'
import LoginSignUp from "./component/User/LoginSignUp";
import store from './store';
import { loadUser } from "./actions/userAction";
import UserOptions from "./component/layout/Header/UserOptions";
import Profile from './component/User/Profile.js'
import ProtectedRoute from "./component/Route/ProtectedRoute";
function App() {
  const { isAuthenticated, user } = useSelector((state) => state.user);
  useEffect(() => {
    WebFont.load({
      google: {
        families: ["Roboto", "Droid Sans", "Chilanka"],
      },
    });

    store.dispatch(loadUser());
  }, []);

  window.addEventListener("contextmenu", (e) => e.preventDefault());

  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/>} />
          <ProtectedRoute exact path='/account' element={<Profile/>} />
          <Route exact path='/login' element={<LoginSignUp/>} />
        </Routes>
      <Footer/>
    </Router>
  );
}

export default App;

And ProtectedRoute.js file

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

const ProtectedRoute = ({ isAdmin, component: Component, ...rest }) => {
  const { loading, isAuthenticated, user } = useSelector((state) => state.user);

  return (
    <Fragment>
      {loading === false && (
        <Route
          {...rest}
          render={(props) => {
            if (isAuthenticated === false) {
              return <Navigate to="/login" />;
            }

            if (isAdmin === true && user.role !== "admin") {
              return <Navigate to="/login" />;
            }

            return <Component {...props} />;
          }}
        />
      )}
    </Fragment>
  );
};

export default ProtectedRoute;

Please help me out ... Thank you..

CodePudding user response:

I think the Route component needs to be a direct child of routes but you could wrap the Profile component in a Protected component maybe?

Something like:

const Protected = ({ isAdmin, component: Component, ...routeProps }) => {
  const { loading, isAuthenticated, user } = useSelector((state) => state.user);

  if (!loading && isAuthenticated === false) {
    return <Navigate to="/login" />;
  }

  if (!loading && isAdmin === true && user?.role !== "admin") {
    return <Navigate to="/login" />;
  }

  return (
    <Fragment>
      {loading === false ? (
       <Component {...routeProps} />
      ) : null}
    </Fragment>
  );
};

And then you should be able to use it like this:

<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={<Protected component={Profile} />} />
  <Route exact path='/login' element={<LoginSignUp/>} />
</Routes>

That example assumes any props you pass into the Protected component would then get passed down to the child.

Alternatively you could just render the child component inside of Protected:

<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={
            <Protected>
                <Profile />
            </Protected>
        }
    />
    <Route exact path='/login' element={<LoginSignUp />} />
</Routes>

In which case the Protected component would look something like this:

const Protected = ({ isAdmin, children }) => {
    const { loading, isAuthenticated, user } = useSelector(state => state.user)

    if (!loading && isAuthenticated === false) {
        return <Navigate to='/login' />
    }

    if (!loading && isAdmin === true && user.role !== 'admin') {
        return <Navigate to='/login' />
    }

    return <Fragment>{loading === false ? children : null}</Fragment>
}

Honestly though, there's really quite a few ways to handle authenticated routing architecture.

  • Related