Mi solución
if(route.to == '/login' && auth.user){
return null
}
Fundamentos de navegación en la web
¿Cuándo necesitas React Router?
SSR vs. Single Page Applications
Versiones de React Router: ¿Por qué son tantas? ¿Cuál elegir?
Introducción a React Router DOM 6
Instalación de React Router DOM 6
BrowserRouter vs. HashRouter
Route: componentes de navegación
Link vs. NavLink
useParams: rutas dinámicas
useNavigate: historial de navegación
Outlet: nested routes
Fake authentication con React Router DOM 6
useAuth: login y logout
Menú con rutas públicas y privadas
Navigate y redirects: protegiendo rutas privadas
Roles y permisos
Reto: composición de componentes con navegación
Reto: UX de login y logout
Reto: roles complejos
React Router en TODO Machine
Integrando React Router a proyectos en React
Creando las rutas de TODO Machine
Botón de editar TODOs
Generador automático de IDs
Cambiando modales por navegación
Obtener y editar TODOs
useLocation: transferencia de datos por navegación
Deploy con React Router en GitHub Pages
Próximos pasos
Reto: página de búsquedas con navegación
Reto: TODO Machine con React Router DOM 5
Reto: PlatziMovies con React Router
Reto: crea tu propio React Router
Espera más cursos de React.js
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Convierte tus certificados en títulos universitarios en USA
Antes: $249
Paga en 4 cuotas sin intereses
Termina en:
Juan David Castro Gallego
Aportes 63
Preguntas 1
Mi solución
if(route.to == '/login' && auth.user){
return null
}
Para que no se pueda entrar directamente con url se debe crear un componente que envuelva las rutas privadas y dependiendo si ya hicieron login renderizarlas o no.
Ahora vamos a hacer que nuestro menú no muestre los enlaces para hacer es crear las validaciones de si un usuario NO está registrado, no deberíamos mostrar una página de logout o perfil, y si está registrado, entonces no deberíamos mostrar la pestaña de login.
Para esto en nuestro menú debemos validar si tenemos un usuario registrado, o no:
Main.js
/* En cada una de nuestras rutas vamos a crear una nueva propiedad
que se llame "private" que nos permita validár si cierta secciones
pública o privada */
const routes = [];
routes.push({
to: '/',
text: 'Home',
private: false,
});
routes.push({
to: '/blog',
text: 'Blog',
private: false,
});
routes.push({
to: '/profile',
text: 'Profile',
private: true,
});
routes.push({
to: '/login',
text: 'Login',
private: false,
});
routes.push({
to: '/logout',
text: 'Logout',
private: true,
});
Teniendo esto ya podemos validar si renderizamos cierto componente o no lo hacemos; vamos entonces a crear la validación que nos permita lograr esto:
function Menu() {
const auth = useAuth();
return (
<nav>
<ul>
{/* cambiamos el return inferido por unas llaves y el
return */}
{routes.map(route => {
/* Este contenido se va a renderizar solo si se pasa la
siguiente validación, si no estamos autenticados y
tratamos de entrar a un a ruta privada entonces no vamos a
renderizar esa ruta */
if (route.private && !auth.user) return null
return (
<li key={route.to}>
...
</li>
);
})}
</ul>
</nav>
);
}
Listo, ahora lo único de nos falta es que cuando estemos registrados, la manera que yo encontré es la siguiente:
function Menu() {
...
return (
<nav>
<ul>
{routes.map(route => {
...
/* Validamos si hay un usuario registrado, y si el nombre
de la ruta es igual a Login entonces retornamos un "null" */
if (auth.user && route.text === 'Login') return null
return (
<li key={route.to}>
...
</li>
);
})}
</ul>
</nav>
);
}
Ahora lo único que nos falta es que no podamos acceder a las rutas por medio de la barra de búsqueda con Hashes, ya que si accedemos a una ruta que no se renderiza la aplicación se rompe.
Para solucionarlo simplemente agregué un segundo condicional después para saber si la ruta es Login y se está autenticado, quedarían así:
if(route.private && !auth.user) return null;
if(route.text === 'Login' && auth.user) return null;
Amo como explica el profe ❤️❤️❤️
Mi solución fue la siguiente:
if ((route.private && !auth.user) || (route?.publicOnly && !!auth.user)) return null;
De esta manera logré aplicar un solo condicional y reutilizar el código ya existente
Tenia un poco de dudas del funcionamiento de los return, pero cuando se manejan bien ahorras mucho código.
if (auth.user && route.text ==='Login') return null;
.
Para simular tener rutas públicas y privadas en nuestro Menu
, necesitamos añadir algunas propiedades a nuestro array de rutas.
.
primero agregamos la propiedad private
para indicar si una ruta es privada o no. Luego a la ruta /login
le añadimos la propiedad publicOnly
que nos ayudará a ocultar esta ruta cuando estemos logueados pero siga siendo una ruta pública.
.
const routes = [];
routes.push({
to: '/',
text: 'Home',
private: false,
});
routes.push({
to: '/blog',
text: 'Blog',
private: false,
});
routes.push({
to: '/profile',
text: 'Profile',
private: true,
});
routes.push({
to: '/login',
text: 'Login',
private: false,
publicOnly: true,
});
routes.push({
to: '/logout',
text: 'Logout',
private: true,
});
.
Finalmente, agregamos 2 condicionales para que al momento de renderizar las rutas:
.
.
import React from 'react';
import { NavLink } from 'react-router-dom';
import { useAuth } from './auth';
function Menu() {
const auth = useAuth();
return (
<nav>
<ul>
{routes.map(route => {
if (route.publicOnly && auth.user) return null;
if (route.private && !auth.user) return null;
return (
<li key={route.to}>
<NavLink
style={({ isActive }) => ({
color: isActive ? 'red' : 'blue',
})}
to={route.to}
>
{route.text}
</NavLink>
</li>
);
})}
</ul>
</nav>
);
}
...
export { Menu };
.
Otra forma de renderizar las rutas sin tener que crear una propiedad publicOnly
sería verificar si estamos logueados y la ruta corresponde a /login
.
.
import React from 'react';
import { NavLink } from 'react-router-dom';
import { useAuth } from './auth';
function Menu() {
const auth = useAuth();
return (
<nav>
<ul>
{routes.map(route => {
if (!auth.user && route.private) return;
if (auth.user && route.to === '/login') return;
return (
<li key={route.to}>
<NavLink
style={({ isActive }) => ({
color: isActive ? 'red' : 'blue',
})}
to={route.to}
>
{route.text}
</NavLink>
</li>
);
})}
</ul>
</nav>
);
}
const routes = [];
routes.push({
to: '/',
text: 'Home',
private: false,
});
routes.push({
to: '/blog',
text: 'Blog',
private: false,
});
routes.push({
to: '/profile',
text: 'Profile',
private: true,
});
routes.push({
to: '/login',
text: 'Login',
private: false,
});
routes.push({
to: '/logout',
text: 'Logout',
private: true,
});
export { Menu };
My solution
if(auth.user && route.text === 'Login') return null
No se si es la mas elegante de las soluciones, pero…
if (auth.user && route.text==="Login") return null
Esta fue mi solucion al reto de JuanDC:
{
routes.map(route => {
if (route.private && !auth.user) return null
if (auth.user && route.to.includes('login')) return null
return (
<li key={route.to} >
<NavLink
style={({ isActive }) => ({
color: isActive ? 'red' : 'blue',
})}
to={route.to}
end
>
{ route.text}
</NavLink>
</li>
)
})
}
Esta opción funcionará siempre y cuando la ruta del login incluya las palabras “login”.
Escribiendo esto creo que la opción de Juan tiene mucho más sentido, pero bueno, esa se me ocurrió a mí.
La solución que implementé yo fue:
if(auth.user && route.to.includes("/login")) return null;
Pero la solución de Juan es mucho mas escalable para el futuro en caso de que quiera agregar muchas más rutas que sean sólamente publicas. Excelente!
Para solucionar el problema de que pongan la ruta /profile
y no haya iniciado sesión, usé esta estrategia:
En ProfilePage.jsx
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../../auth/useAuth';
import { useEffect } from 'react';
export const ProfilePage = () => {
const { user } = useAuth();
const navigate = useNavigate();
useEffect(() => {
if (user === null) return navigate('/login');
});
return (
<div>
<h2>Profile</h2>
<p>Welcome, {user?.username}!</p>
</div>
);
};
jajajaja me gusta más tu solución, xD yo estaba haciendo una función para que cuando se return - el estado private de login cambiara a true - ya iban 11 líneas de código 😣🙄
Yo el último reto lo resolví con useEffect.
Login:
React.useEffect(()=>{
if(auth.user){
return navigate('/');
}
},[auth.user]);
Logout:
React.useEffect(()=>{
if(!auth.user){
return navigate('/login')
}
},[auth.user]);
yo lo había hecho así
if(route.to === '/login' && auth.user) {
return (route.private = true)
} else {route.private = false}
Buenas, aqui dejo mi solucion a el problema de mostrar unicamente el login cuando no se este logeado un usuario
My solution
Mi solución
if (route.private === "out" && auth.user) return null;
let routes = [
{
to: "/",
text: "Home",
private: "both",
},
{
to: "/blog",
text: "Blog",
private: "both",
},
{
to: "/profile",
text: "Profile",
private: "in",
},
{
to: "/login",
text: "Login",
private: "out",
},
{
to: "/logout",
text: "Logout",
private: "in",
},
];
yo lo hice de esta forma sin necesidad de añadir las propiedades private y publicOnly al arreglo de objetos
if (!user && (route.to === '/profile' || route.to === '/logout')){
return
}
if (user && route.to === '/login'){
return
}
Mi solución al reto
mi solución
<code>
<ul className="nav">
<li className="nav-item">
{routes.map(route =>
(
(user && !route.publicOnly) || (!user && !route.private) ?
<NavLink
key={route.to}
to={route.to}
className={({isActive})=>(`nav-link ${isActive? 'active' : ''}`)}
>
{route.text}
</NavLink> : null
)
)}
</li>
</ul>
if((route.private && !auth.user) || (route.to === '/login' && auth.user)) return null;
La solucion que hice:
if(auth.user && route.text === "Login") return null;
Mi solución es la siguiente; no sé si tenga algún problema más adelante pero pensando desde ahora no lo creo.
if (route.textContext === 'Login' && auth.user) return null;
// Mi arreglo con los objetos 👇👇
const routes = [];
routes.push({
id: 1,
to: '/',
textContent: 'Home',
private: false,
});
routes.push({
id: 2,
to: '/blog',
textContent: 'Blog',
private: false,
});
routes.push({
id: 3,
to: '/profile',
textContent: 'Profile',
private: true,
});
routes.push({
id: 4,
to: '/login',
textContent: 'Login',
private: false,
});
routes.push({
id: 5,
to: '/logout',
textContent: 'Logout',
private: true,
});
Si hay algún problema, estoy dispuesto a escuchar feedback.
Saludos.
Pensando en el caso de Freddy que es un superadmin, debería tener rutas que solo el puede entrar.
Por esa razon cambie lo de public y private por un route type, para que no nos crezca de manera vertical el objeto al agregar otro typo de ruta.
const Menu = () => {
const auth = useAuth();
return (
<nav>
<ul>
{routes.map((route) => {
if (route.routeType === routeTypes.private && !auth.user) return null;
if(route.routeType === routeTypes.onlyPublic && auth.user) return null
return (
<li key={route.to}>
<NavLink to={route.to}>{route.text}</NavLink>
</li>
);
})}
</ul>
</nav>
);
};
const routeTypes = {
private: 'PRIVATE',
onlyPublic: '0NLY-PUBLIC',
all:'ALL'
}
const routes = [];
routes.push({
routeType: routeTypes.all,
to: "/",
text: "Home",
});
routes.push({
routeType: routeTypes.private,
to: "/profile",
text: "Profile",
});
routes.push({
routeType: routeTypes.all,
to: "/blog",
text: "Blog",
});
routes.push({
routeType: routeTypes.onlyPublic,
to: "/login",
text: "Login",
});
routes.push({
routeType: routeTypes.private,
to: "/logout",
text: "Logout",
});
import React from 'react';
import {NavLink} from "react-router-dom";
import routes from '../fixtures/routes';
import {useAuth} from "./auth/auth";
function Menu() {
const auth = useAuth();
const checkIsActive = (active) => {
return {color: (active) ? 'green' : 'red'};
};
const isOnlyPublicRoute = (route) => {
return !(route?.onlyPublic && auth.user);
};
const isPrivateRoute = (route) => {
return !(route.private && !auth.user);
};
return (
<nav>
<ul>
{
routes.map(route => {
if (isOnlyPublicRoute(route)) {
if (isPrivateRoute(route)) {
return (<li key={route.id}>
<NavLink
to={route.to}
end
style={({isActive}) => (checkIsActive(isActive))}
>
{route.text}
</NavLink>
</li>);
}
}
})
}
</ul>
</nav>
);
}
export {Menu};
A mi se me ocurrio esto y funciono. =)
Yo había intentando hacer push a según que ruta dependiendo de si estamos autentificados o no. El problema es que el render no cambia ya que el array tampoco lo hace.
Las soluciones de los comentarios sobre comprobar directamente si la ruta es login no me parecen convenientes. Si queremos cambiar algo de esa ruta en algún momento, tendríamos que cambiar también el condicional.
Finalmente decidí agregar una propiedad ‘onlyIfNotAuth’ a login (no la cambié porque creo que el nombre ‘publicOnly’ es confuso).
Mi solución:
else if (route.to === '/login' && auth.user?.username) return null;
Mi solución:
if (auth.user && route.to === '/login') return null;
if (route.to !== "/login" && auth.user) {
return (
<li key={route.to}>
<NavLink
style={({ isActive }) => ({
color: isActive ? "red" : "blue",
})}
to={route.to}
>
{route.text}
</NavLink>
</li>
);
}
Para mi fue simple y obvio
if (route.text === 'Login' && auth.user) return null
function Menu() {
const auth = useAuth();
return (
<NavLink>
{routes.map((route) => {
if (route.private && !auth.isUsernameLog) return null;
return (
<ul>
{auth.isUsernameLog && route.to === "/login" ? null : (
<li>
<Link to={route.to}>{route.title}</Link>
</li>
)}
</ul>
);
})}
</NavLink>
);
}```
Con el ultimo challenge
function Menu() {
const auth = useAuth();
const location = useLocation();
const navigate = useNavigate();
return (
<NavLink>
{routes.map((route) => {
if (route.private && !auth.isUsernameLog) return null;
if (location.pathname === "/login" && auth.isUsernameLog)
return navigate("/profile");
if (location.pathname === "/profile" && !auth.isUsernameLog)
return navigate("/");
return (
<ul>
{auth.isUsernameLog && route.to === "/login" ? null : (
<li>
<Link to={route.to}>{route.title}</Link>
</li>
)}
</ul>
);
})}
</NavLink>
);
}
Creo que la forma mas sencilla es invertir la condicional y funciona de marivilla.
{authentications.map((authentication) => {
if (authentication.private && !auth.user) return null;
if (!authentication.private && auth.user) return null;
return (
<li>
<NavLink to={authentication.to}>{authentication.text}</NavLink>
</li>
);
})}
#setotea
Creo que no fue lo mas eficiente, pero a mi me funciono de esta forma jejejee
const Menu = () => {
const { auth } = useAuth();
return (
<nav>
<ul className=''>
{routes.map((route, index) => {
if (!route.private && !auth?.username) {
return <MenuItem key={route.text} route={route} />;
}
if (route.private && auth?.username) {
return <MenuItem key={route.text} route={route} />;
}
})}
</ul>
</nav>
);
};
Para el reto coincidí con el profesor y lo resolví de la misma forma, pero también se me ocurrieron otras:
if( route.text === 'Login' && auth.user ) return null;
O también:
if( route.to === '/login' && auth.user ) return null;
Aunque a mi parecer estas son menos dinámicas, ya que si se cambia el nombre o la ruta, habrá que hacer el cambio en el condicional también.
pensaba que con el return ya se finalazaba la ejecucion del codigo… pero se segun GPT el return null dentro del map afecta solo al elemento actual, no al resto de los elementos del array. por lo tanto luego del return; map continua con la ejecucion del codigo. esto aplica tanto para JSX como para javascript vanilla.
8:39 me saco un susto con audífonos profe.
Solución utilizando un custom “middleware” el cual validara los datos antes de renderizar el contenido que le pasemos. Funciona como un componente padre que si todas las validaciones están bien renderiza al hijo y así protegemos todas las rutas que queramos validar sesión con 1 sola función.
Archivo protectedRoutes.jsx
import React from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "../Components/auth";
const ProtectedRoutes = ({children}) =>{
const navigate = useNavigate();
const { user } = useAuth();
React.useEffect(()=>{
if (!user?.username) return navigate('/login')
}, [])
return children
}
export { ProtectedRoutes }
Archivo ProfilePage.jsx
import React from "react";
import { ProtectedRoutes } from "../middlewares/protectedRoutes";
import { useAuth } from "./auth";
const ProfilePage = () =>{
const auth = useAuth();
return (
<>
<ProtectedRoutes>
<h1>Welcome!</h1>
<p>{auth.user?.username}</p>
</ProtectedRoutes>
</>
)
}
export { ProfilePage }
Mi solución fue añadir un condicional más
if(route.text == "Login" && auth.user) return null
if ((route.publicOnly && auth.user) || (route.private && !auth.user))
return null;
para @juandc, que es un crack explicando, pero siempre tiene dudas de que usan los objetos.
los objetos usan LLAVES.
los objetos contienen LLAVE y valor, y se separan con LLAVES {}
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?