Navigate y redirects: protegiendo rutas privadas
Como mencionamos anteriormente, si un usuario intenta acceder a una ruta privada por medio del historial o la barra de búsqueda aún si esta no esta renderizada o no puede acceder lo que va a causar es que nuestra aplicación este en blanco, colapse y ya no se pueda interactuar hasta que recarguemos.
Para solucionar esto lo que haremos es protegerlas de verdad haciendo que si intentan acceder a una ruta parvada haga un redirect al home, a login o a profile dependiendo de la ruta donde intente acceder.
Para esto vamos primero a nuestro Profile.js:
// Importamos el componente navigate
import { useAuth } from '../../auth/auth';
function ProfilePage() {
const auth = useAuth();
/* Preguntamos si hay un usuario registrado, si no, redirigimos
a la persona */
if (!auth.user) {
/* Con este componente podemos hacer el redirect hacia donde la
lógica de nuestra aplicación lo requiera */
return <Navigate to="/login" />
}
return (
<>
<h1>Perfil</h1>
<h1>Welcome {auth.user.username}</h1>
</>
);
}
De esta manera si no nos hemos registrado e intentamos acceder a Profile por medio de la barra de búsqueda nos redirigirá a nuestro login.
En caso de que necesitemos redireccionar varias veces a distintos sitios de nuestra aplicación veremos que hacer esto puede ser un poco repetitivo, debemos solucionar esto.
Vamos a auth.js y creemos una función que nos permita hacer esto más sencillo.
import { Navigate } from 'react-router-dom';
...
/* En este compoinente podemos hacer la validación que es la misma
que utilizamos con anterioridad */
function AuthRoute(props) {
const auth = useAuth();
if (!auth.user) {
return <Navigate to="/login" />
}
return props.children
}
export {
...
AuthRoute,
}
Luego simplemente implementamos este componente en el ProfilePage.js:
import { AuthRoute, useAuth } from '../../auth/auth';
function ProfilePage() {
const auth = useAuth();
return (
<AuthRoute>
<h1>Perfil</h1>
<h1>Welcome {auth.user.username}</h1>
</AuthRoute>
);
}
export { ProfilePage }
Si lo ejecutamos tendremos un error debido a que al renderizar el componente aún así lo redireccionemos como auth.user.username
no existe y esto nos dará un error ya que en el componente lo estamos solicitando y esto nos genera un erro bloqueante al redireccionar a login.
Pero para que funcione esto en nuestro llamado desde App.js debemos importar a este componente ProfilePage dentro de un componente AuthRoute:
import { AuthProvider, useAuth, AuthRoute } from './Components/auth/auth'
...
function App() {
return (
<>
<HashRouter>
<AuthProvider>
<Routes>
...
<Route path='/profile'
element={
<AuthRoute>
<ProfilePage /> {/* <-- */}
</AuthRoute>
}
/>
</Routes>
</AuthProvider>
</HashRouter>
</>
)
}
Y nuestro componente ProfilePage.js lo podemos dejar como estaba:
function ProfilePage() {
const auth = useAuth();
return (
<>
<h1>Perfil</h1>
<h1>Welcome {auth.user.username}</h1>
</>
);
}
Ahora haremos lo mismo con el Logout en App.js:
<Route path='/logout'
element={
<AuthRoute>
<LogoutPage />
</AuthRoute>
}
/>
Lo único que nos queda por hacer es que cuando estemos autenticados no podamos acceder a la página de Login, porque estamos protegiendo las rutas privadas de un usuario público pero no la ruta pública de registro a un usuario privado. ¡Hagamos esto!
LoginPage.js
function LoginPage() {
...
/* Como en este caso solo necesiamos hacer una validación lo
haremos directo en el componente preguntando si el usuario ya esta
registrado, si lo está lo redirigiremos a la página de Profile
usando el Navigate */
if(auth.user) {
return <Navigate to="/profile" />
}
return (
...
)
}
Y hemos terminado de proteger nuestras rutas y tener una mejor lógica de aplicación.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?