javascript - Error: [PrivateRoute] no es un componente <Route>. Todos los componentes hijos de <Routes> deben ser un <Route> o <React.Fragment>
Estoy usando React Router v6 y estoy creando rutas privadas para mi aplicación.
En el archivo PrivateRoute.js, tengo el código
import React from 'react';
import {Route,Navigate} from "react-router-dom";
import {isauth} from 'auth'
function PrivateRoute({ element, path }) {
const authed = isauth() // isauth() returns true or false based on localStorage
const ele = authed === true ? element : <Navigate to="/Home" />;
return <Route path={path} element={ele} />;
}
export default PrivateRoute
Y en el archivo route.js he escrito como:
...
<PrivateRoute exact path="/" element={<Dashboard/>}/>
<Route exact path="/home" element={<Home/>}/>
He revisado el mismo ejemplo React-router Auth Example - StackBlitz, archivo App.tsx
¿Hay algo que me esté perdiendo?
Respuesta #1
Me encontré con el mismo problema hoy y surgió la siguiente solución basada en este artículo muy útil de Andrew Luca
En PrivateRoute.js:
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
const PrivateRoute = () => {
const auth = null; // determine if authorized, from context or however you're doing it
// If authorized, return an outlet that will render child elements
// If not, return element that will navigate to login page
return auth ? <Outlet /> : <Navigate to="/login" />;
}
En App.js (he dejado otras páginas como ejemplos):
import './App.css';
import React, {Fragment} from 'react';
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Home from './components/pages/Home';
import Register from './components/auth/Register'
import Login from './components/auth/Login';
import PrivateRoute from './components/routing/PrivateRoute';
const App = () => {
return (
<Router>
<Fragment>
<Navbar/>
<Routes>
<Route exact path='/' element={<PrivateRoute/>}>
<Route exact path='/' element={<Home/>}/>
</Route>
<Route exact path='/register' element={<Register/>}/>
<Route exact path='/login' element={<Login/>}/>
</Routes>
</Fragment>
</Router>
);
}
En el enrutamiento anterior, esta es la ruta privada:
<Route exact path='/' element={<PrivateRoute/>}>
<Route exact path='/' element={<Home/>}/>
</Route>
Si la autorización tiene éxito, el elemento se mostrará. De lo contrario, se navegará a la página de inicio de sesión.
Respuesta #2
Solo los componentes Route pueden ser hijos de Routes. Si sigues la documentación de la versión 6, verás que el patrón de autenticación es usar un componente envolvente para manejar la verificación de autenticación y la redirección.
function RequireAuth({ children }: { children: JSX.Element }) { let auth = useAuth(); let location = useLocation(); if (!auth.user) { // Redirect them to the /login page, but save the current location they were // trying to go to when they were redirected. This allows us to send them // along to that page after they login, which is a nicer user experience // than dropping them off on the home page. return <Navigate to="/login" state={{ from: location }} />; } return children; } ... <Route path="/protected" element={ <RequireAuth> <ProtectedPage /> </RequireAuth> } />
El antiguo patrón v5 de crear componentes Route personalizados ya no funciona. Un patrón actualizado de v6 usando tu código/lógica podría verse de la siguiente manera:
const PrivateRoute = ({ children }) => {
const authed = isauth() // isauth() returns true or false based on localStorage
return authed ? children : <Navigate to="/Home" />;
}
Y usar
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
Respuesta #3
Complemento para reducir líneas de código, hacerlo más legible y hermoso.
Esto podría ser solo un comentario, pero no tengo suficientes puntos, así que lo pondré como una respuesta.
¡La respuesta de Dallin funciona, pero la respuesta de Drew es mejor! Y solo para completar la respuesta de Drew sobre estética, recomiendo crear un componente privado que reciba componentes como props en lugar de como hijos.
Ejemplo muy básico de archivo/componente de rutas privadas:
import { Navigate } from 'react-router-dom';
const Private = (Component) => {
const auth = false; //your logic
return auth ? <Component /> : <Navigate to="/login" />
}
Ejemplo de archivo de ruta:
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/user" element={<Private Component={User} />} />
</Routes>
Respuesta #4
Sé que esta no es exactamente la receta sobre cómo hacer que PirvateRoute funcione, pero solo quería mencionar que la nueva documentación recomienda un enfoque ligeramente diferente para manejar este patrón con react-router v6:
<Route path="/protected" element={<RequireAuth><ProtectedPage /></RequireAuth>} />
import { Navigate, useLocation } from "react-router";
export const RequireAuth: React.FC<{ children: JSX.Element }> = ({ children }) => {
let auth = useAuth();
let location = useLocation();
if (!auth.user) {
return <Navigate to="/login" state={{ from: location }} />;
}
return children;
};
Y se supone que debes agregar más rutas dentro de ProtectedPage si las necesitas.
Consulta la documentación y un ejemplo para más detalles. Además, revisa esta nota de Michael Jackson que entra en algunos detalles de implementación.
