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.