Fundamentos de navegación en la web

1

Navegación Web con React Router: Fundamentos y Prácticas Avanzadas

2

Server-side Rendering vs Single Page Applications: Ventajas y Desventajas

3

Uso de React Router DOM 6 en Proyectos React

Introducción a React Router DOM 6

4

Instalación de React Router DOM 6 en un proyecto React

5

Uso de Hash Router en Aplicaciones Web con React Router DOM

6

Creación de Rutas Dinámicas con React Router DOM 6

7

Navegación en React Router: Uso de Link y NavLink

8

Rutas dinámicas con React Router DOM y useParams

9

Uso de useNavigate en React Router DOM para navegación dinámica

10

Uso de Outlet y Nested Routes en React Router DOM 6

Fake authentication con React Router DOM 6

11

Autenticación y Autorización en Apps con React Router y Hooks

12

Control de Acceso en Menú con Autenticación React

13

Protección de Rutas con React Router y Hooks

14

Roles y permisos en aplicaciones web: Autenticación y autorización

15

Retos avanzados en React: manejo de estado y composición de componentes

16

Mejorando la Redirección Post-Login en Aplicaciones Web

17

Roles y Permisos Avanzados en React Router v6

React Router en TODO Machine

18

Migración de Todo Machine a React Router 6

19

Organización de carpetas y rutas en React con React Router DOM 6

20

Maquetación de Botón Editar en Lista de Tareas con React

21

Generación de IDs únicos para gestionar tareas en React

22

Migración de modales a rutas en React: implementación práctica

23

Editar ToDos en React con Custom Hook y URL Parameters

24

Mejora de la Experiencia del Usuario al Editar To Do's en React

25

Implementación de React Router en Proyectos Legacy

Próximos pasos

26

Filtrado de Búsquedas en URL con React Router

27

Migración de React Router: de la versión 6 a la 5 en proyectos empresariales

28

Clonación de Platzi Movies usando React y React Router

29

Clonación de React Router en Componentes React

30

Navegación Avanzada con React Router DOM 6

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Autenticación y Autorización en Apps con React Router y Hooks

11/30
Recursos

¿Cómo implementar un flujo de autenticación y autorización en aplicaciones?

En este emocionante recorrido, vamos a construir un flujo completo de navegación para aplicaciones que requieren autenticación y autorización utilizando React Router DOM. Aprenderemos a manejar rutas privadas y proteger contenido según el estado de autenticación del usuario.

¿Qué es la autenticación y autorización en aplicaciones web?

  • Autenticación: Es el proceso de verificar la identidad de un usuario. Un ejemplo común es el inicio de sesión en una aplicación.
  • Autorización: Es la verificación de los permisos que tiene un usuario autenticado para acceder a ciertos recursos o realizar acciones específicas.

En nuestro sistema, las rutas privadas estarán accesibles solo para usuarios autenticados, mientras que otras funcionalidades podrán variar dependiendo del rol del usuario.

¿Cómo simular la autenticación?

Hemos creado un sistema de autenticación ficticio para ilustrar cómo se podría implementar un flujo de autenticación. Inicialmente, esto se gestiona con React Hooks para simular un "login falso".

Pasos para crear la autenticación:

  1. Crear componentes de Login y Logout:

    • Desarrollar una estructura básica para las páginas de login y logout, replicando componentes de ejemplo, y aplicar la lógica de React Router para gestionar las rutas.
  2. Implementar un formulario de login:

    • Añadir un formulario que capture el nombre de usuario e integre un estado básico con useState para gestionar los cambios en el input.
import React, { useState } from 'react';

function LoginPage() {
  const [username, setUsername] = useState('');

  const handleLogin = (event) => {
    event.preventDefault();
    console.log(username); // Simulación de autenticación
  };

  return (
    <form onSubmit={handleLogin}>
      <label>Escribe tu nombre de usuario</label>
      <input 
        type="text" 
        value={username} 
        onChange={(e) => setUsername(e.target.value)} 
      />
      <button type="submit">Entrar</button>
    </form>
  );
}
  1. Crear un contexto para la autenticación:
    • Utilizar React.createContext para establecer un contexto que gestione el estado de autenticación y proporcionar funciones de login y logout.
import React, { createContext, useState, useContext } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  const login = (username) => setUser({ username });
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

¿Cómo integrar autenticación con rutas protegidas?

Una vez que se ha establecido un contexto para la autenticación, podemos utilizarlo para proteger rutas y condicionar la navegación según el estado de login del usuario.

Paso a paso:

  1. Utilizar el AuthProvider en tu aplicación:
    • Encapsular la estructura de la aplicación dentro del AuthProvider para que todas las rutas tengan acceso al contexto de autenticación.
import { AuthProvider } from './auth';

function App() {
  return (
    <AuthProvider>
      <HashRouter>
        {/* Rutas de tu aplicación */}
      </HashRouter>
    </AuthProvider>
  );
}
  1. Proteger rutas sensibles:
    • Condicionar el acceso a ciertas rutas verificando el estado de usuario en el contexto. Si no está autenticado, redirigir al login.
import { useAuth } from './auth';

function ProfilePage() {
  const auth = useAuth();

  if (!auth.user) {
    return <Redirect to="/login" />;
  }

  return <h1>Bienvenido, {auth.user.username}</h1>;
}

¿Cómo manejar la visibilidad de enlaces según el estado de autenticación?

Para mejorar la experiencia del usuario, es crucial ajustar la visibilidad de los enlaces de navegación según si el usuario está autenticado o no.

  1. Actualizar el menú de navegación:
    • Cambiar dinámicamente las opciones de navegación basándose en si el usuario ha iniciado sesión.
function Navigation() {
  const auth = useAuth();

  return (
    <nav>
      {!auth.user ? <Link to="/login">Login</Link> : <Link to="/logout">Logout</Link>}
      {auth.user && <Link to="/profile">Profile</Link>}
    </nav>
  );
}

Este enfoque garantiza que los usuarios no autenticados sean llevados a login si intentan acceder a áreas restringidas, y mantiene la interfaz limpia y adecuada a su estado. Este es solo el inicio de un sistema de autenticación robusto; las mejoras podrían incluir gestión de roles más compleja o integración con un backend para autenticación verificada. ¡Sigue explorando más sobre este tema para llevar tus proyectos al siguiente nivel!

Aportes 20

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

useAuth: login y logout

Ahora vamos a aprender como desplegar contenido en base a la Autenticación de usuarios, ósea que no todo el contenido de una aplicación va a ser público pata todos los usuarios, sino que tengamos algunas rutas privadas para personas que no se hayan autenticado, ósea hecho login y así mismo no mostrar contenido que es innecesario para personas que ya están registradas, como el mismo registro de usuario o contenido introductorio.

Vamos a hacer contenido que dependiendo el usuario se muestre o no se muestre, y también permitir tener privilegios o permisos dependiendo del rol que tengas.

Vamos entonces a crear nuevas rutas que nos permitan hacer todo esto:

import { LoginPage } from './Components/Routes/LoginPage/LoginPage';
import { LogoutPage } from './Components/Routes/LogoutPage/LogoutPage';

function App() {
  return (
    <>
      <HashRouter>
				<Routes>
	        ...
          
          <Route path='/login' element={<LoginPage />} /> 
          <Route path='/logout' element={<LogoutPage />} />
          <Route path='/profile' element={<ProfilePage />} />

        </Routes>
      </HashRouter>
    </>
  )
}

Y debemos crear nuestros respectivos componentes:

LoginPage.js

import React from 'react';

function LoginPage() {
  /* Para manejar el registro de usuario con el formilario haremos 
  uso del estado de React */
  const [username, setUsername] = React.useState('');

  /* Esta es la función que se ejecutará cuando ocurra el evento de 
  Submit */
  const login = (e) => {
    e.preventDefault();
  }

  // Aquí creamos un formulario para poder auteticarnos
  return (
    <>
      <h1>Login</h1>

      <form onSubmit={login}>
        <label>Escribe tu nombre de usuario</label>
        <input
          value={username}
          onChange={ e => setUsername(e.target.value)}
        />

        <button type="submit">Entrar</button>

      </form>
    </>
  );
}

export { LoginPage }

LogoutPage.js

import React from 'react';

function LogoutPage() {
  const logout = (e) => {
    e.preventDefault();
  }

  return (
    <>
      <h1>Logout</h1>

      <form onSubmit={logout}>
        <label>¿Segurx de que quieres salir?</label>

        <button type="submit">Salir</button>

      </form>
    </>
  );
}

export { LogoutPage }

En nuestro Menu.js vamos a crear dos nuevas rutas en nuestro Array routes:

...
const routes = [];

...
routes.push({
  to: '/login',
  text: 'Logout'
});
routes.push({
  to: '/logout',
  text: 'Login'
});

Si lo pensamos bien, no nos debería salir el componente Profile y Logout sin antes haber hecho un Login, vamos a trabajar entonces en esa lógica creando un archivo de autenticación:

auth.js

import React from 'react';

const AuthContext = React.createContext();

function AuthProvider({ children }) {
  const auth = {
    user,
    login,
    logout,
  }

  return (
    <AuthContext.Provider
      value={auth}
    >
      {children}
    </AuthContext.Provider>
  )
}

function useAuth() {
  const auth = React.useContext(AuthContext);
  return auth;
}

export {
  AuthProvider,
  useAuth,
}

Vamos a importar esta lógica al App.js:

...
import { AuthProvider, useAuth } from './Components/auth/auth'

function App() {
  return (
    <>
      {/* En este caso AuthProvider tiene que ir dentro de 
      HastRouter en caso de que AuthProvider necesite de algún 
      método o contenido que pueda proveer HashRouter */}
      <HashRouter>
        <AuthProvider>
          <Menu />

          <Routes>
            <Route path='/' element={<HomePage />} />
            <Route path='/blog' element={<BlogPage />}>
              <Route path=':slug' element={<BlogPost />} />
            </Route>
            
            <Route path='/login' element={<LoginPage />} />
            <Route path='/logout' element={<LogoutPage />} />
            <Route path='/profile' element={<ProfilePage />} />

            <Route path='/*' element={<p>Not Found</p>} />

          </Routes>
        </AuthProvider>

      </HashRouter>
    </>
  )
}

...

En este punto, en LoginPage.js ya podemos empezar a utilizar nuestro useAuth:

import { useAuth } from '../../auth/auth';
...

function LoginPage() {
  const auth = useAuth();
	...
}

Vamos de nuevo auth.js y seguimos con la lógica:

...
function AuthProvider({ children }) {
  /* Siguiendo la lógica, si user es null significa que no estamos 
  autenticados */
  const [user, setUser] = React.useState(null);

  // Ahora necesitamos darle un valor a nuestro usuario
  const login = ({ username }) => {
    setUser({ username });
  }

  // De la misma forma, debemos poder cerrar la sesión
  const logout = () => {
    setUser(null);
  }
	...
}

Ahora en LoginPage.js nuestro useAuth nos debe dar acceso al usuario y sus métodos:

function LoginPage() {
  const login = (e) => {
    e.preventDefault();
    auth.login({ username })
  }
}

Vamos de nuevo a auth.js y vamos a complementar la lógica:

function AuthProvider({ children }) {
  const navigate = useNavigate();

  const login = ({ username }) => {
    setUser({ username });
		/* Ahora cada vez que hagamos login nos redireccionará a la 
		página de profile */
    navigate('/profile');
  }

  const logout = () => {
		setUser(null);
    /* Aquí haremos redirect a la página principal */
    navigate('/');
  }

Pero para esto debemos añadir el contexto de autentificación a LogoutPage.js:

function LogoutPage() {
  const auth = useAuth();

  const logout = (e) => {
    e.preventDefault();
    auth.logout();
  }
}

Ahora en nuestra ProfilePage.js debemos completar esta lógica:

import { useAuth } from '../../auth/auth';

function ProfilePage() {
  const auth = useAuth();

  return (
    /* Ahora si nosotros nos registramos deberíamos poder ver 
		nuestro nombre de usuario en nuestra página de perfil */
    <>
      <h1>Perfil</h1>
      <h1>Welcome {auth.user.username}</h1>
    </>
  );
}

Y listo, ahora solo nos queda el que no podamos entrar a profile sino hasta que hayamos hecho login, lo cual haremos a continuación.

Por si alguien tien este error: usando Vite

[vite] Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
  Plugin: vite:import-analysis
  File: /home/naiper/cursos/33-react-router/src/helpers/auth.js
  33 |      logout
  34 |    };
  35 |    return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
     |                                                                                ^
  36 |  }
  37 |
      at formatError (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:40862:46)
      at TransformContext.error (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:40858:19)
      at TransformContext.transform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:37530:22)
      at async Object.transform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:41111:30)
      at async loadAndTransform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:37373:29)

Lo que hice fue cambiar el archivo auth.js por un auth.jsx

Otra solución que encontré fue cambiar la configuración del vite.config.js

Agregué una regla: “Enable hmr overlay with vite react and ts”

Pero lamentablemente no me sirvió, sólamente me quitó el error en Vite pero me dejó en consola.

Decidí organizar los archivos de esta manera.

Mi Profe Facherito

Gracias Juan. De hecho en primera instancia creí que era mejor que el AuthProvider fuera el componente de más arriba. Pero luego de tu explicación, comprendo qué lógica debo tener a la hora de definir cómo organizar estos Providers.

Cuando relacione el label con el input utilizando for, react me sugirió cambiarlo por htmlFor.

  <label htmlFor='input-login'>Escribe tu nombre de usuario:</label>
        <input
        value={username}
        onChange={e => setUsername(e.target.value)}
        id='input-login'/>

Por si alguien también está haciendo el proyecto con TypeScript, comparto cómo me quedó auth.tsx:

import React, { useContext, useState } from 'react'
import type { PropsWithChildren } from 'react'
import { useNavigate } from 'react-router-dom'

interface Auth {
  user: { username: string } | null
  login: (username: string) => void
  logout: () => void
}

const AuthContext = React.createContext({})

function AuthProvider({ children }: PropsWithChildren) {
  const navigate = useNavigate()
  const [user, setUser] = useState<Auth['user']>(null)

  function login(username: string) {
    if (username) 
      setUser({ username })
      navigate('/profile')
  }
  
  function logout() {
    setUser(null)
    navigate('/')
  }

  const auth: Auth = { user, login, logout }

  return (
    <AuthContext.Provider value={auth}>
      { children }
    </AuthContext.Provider>
  )
}

function useAuth() {
  const authContext = useContext(AuthContext) as Auth
  return authContext
}

export { AuthProvider, useAuth }

Código de TypeScript de la clase:
AuthStore.tsx

import { createContext, FC, useState, PropsWithChildren } from 'react'
import { useNavigate } from 'react-router-dom'

interface User {
    username: string
}

interface Auth {
    user: User | null
    login: (user: User) => void
    logout: () => void
}

// interface Auth {
//     user: { username: string } | null
//     login: (user: Auth['user']) => void
//     logout: () => void
// }

const AuthContext = createContext({} as Auth);

const AuthProvider:FC<PropsWithChildren> = ({ children }) => {

    const navigate = useNavigate();
    const [user, setUser] = useState<Auth['user']>(null);

    const login:Auth['login'] = (user) => {
        setUser(user);
        navigate('/profile')
    }

    const logout:Auth['logout'] = () => {
        setUser(null);
        navigate('/');
    }

    const auth:Auth = { user, login, logout }

    return (
        <AuthContext.Provider value={auth}>
            { children }
        </AuthContext.Provider>
    )
}

export { AuthProvider, AuthContext }

useAuth.ts

import { useContext } from "react"
import { AuthContext } from "./AuthStore"

const useAuth = () => {
    const auth = useContext(AuthContext);
    return auth;
}

export { useAuth }

ProfilePage

import { useAuth } from "./useAuth"

const ProfilePage = () => {

    const { user } = useAuth();

    return(
        <>
            <h1>Perfil</h1>
            <p>Welcome, { user?.username} </p>
        </>
    )
}

export { ProfilePage }

Podrían verificar que que exista un usuario con:

 <h1>{auth.user?.username}</h1>

el operador ‘?.’ verifica que dentro de objeto auth.user exista username, si no existe devolverá undefined y el h1 en la pagina estará vació.
.
o podrían hacer una condicional para mostrar un username por default:

const username = auth.user?.username ?? 'User936'

return (
  <h1>{username}</h1>
)

El operador de coalescencia nula ?? solo devuelve el valor del lado derecho (‘User936’) si el valor del lado izquierdo es null o undefined.
Y si no existe un ‘username’ en ‘auth.user?.username’ devolverá undefined

Esta genial el curso Juan pero más de 20 min de fake auth no me sirve. 😦

Esta clase estuvo bien intensa, tuve que mirarla 3 veces para poder entender bien.

Basicamente lo que hace el metodo “login” es setear el estado de user name con el nombre de usuario que le pasamos, que viene del input
El metodo “logout” lo que hace es setear ese estado como null
Y todo esta guardado en el contexto al cual tenemos acceso con el hook de useAuth
Así lo compartimos con todos los componentes

AuthConsumer pensé en OutConsumer uwu, si alguien jugaba NBA en PlayStation allá por el año 2012 de repente conoce a ese Youtuber jajaja

useAuth: login y logout

.
Empezamos a desarrollar un flujo de navegación completo para aplicaciones que tengan autenticación y autorización. Esto significa que no todos los usuarios podrán tener acceso a rutas que deberían de ser privadas u ocultas si no se han autenticado. Incluso si ya hicieron login hay cosas que no queremos que vean, contenido que no cualquier usuario debe poder ver por cuestiones de autorización.
.
Lo primero que haremos en App es crear 2 nuevas rutas /login y logout con sus respectivos componentes a renderizar.
.

<Route path="/login" element={<LoginPage />} />
<Route path="/logout" element={<LogoutPage />} />

.
En Menu también añadimos estas 2 nuevas rutas al array de rutas.
.

const routes = [];
routes.push({
  to: '/',
  text: 'Home',
});
routes.push({
  to: '/blog',
  text: 'Blog',
});
routes.push({
  to: '/profile',
  text: 'Profile',
});
routes.push({
  to: '/login',
  text: 'Login',
});
routes.push({
  to: '/logout',
  text: 'Logout',
});

.
Vamos a necesitar un archivo auth.js el cual nos va permitir simular nuestro flujo de autenticación. Su objetivo principal es exportar un objeto que contenga nuestro AuthProvider y nuestro react hook useAuth.
.
En este archivo creamos un contexto para la autenticación. Dentro del provider de este contexto por medio de la propiedad value le pasamos un objeto auth que va a contener el estado de nuestro usuario user que por defecto es null cuando no está logeado; también contiene a las funciones de login y logout que lo que hacen es setear el estado del usuario y navigar como corresponda mediante useNavigate.
.

import React, { useContext } from 'react';
import { useNavigate } from 'react-router-dom';

const AuthContext = React.createContext();

function AuthProvider({ children }) {
  const navigate = useNavigate();
  const [user, setUser] = React.useState(null);

  const login = ({ username }) => {
    setUser({ username });
    navigate('/profile');
  };
  
  const logout = () => {
    setUser(null);
    navigate('/');
  };
  
  const auth = { user, login, logout };

  return (
    <AuthContext.Provider value={auth}>
      {children}
    </AuthContext.Provider>
  );
}

function useAuth() {
  const auth = React.useContext(AuthContext);
  return auth;
}

export {
  AuthProvider,
  useAuth,
};

.
El react hook useAuth lo que hace es simplemente llamar al contexto, guardarlo y exportarlo en una variable.
.
Finalmente, el AuthProvider es un componente que nos permitirá encapsular el contenido de nuestro HashRouter en App. Por lo que tendrá acceso a la información que puede proporcionar HashRouter, pero a la vez los componentes contenidos por AuthProvider como Menu o las rutas, tendrán acceso a las propiedades del objeto auth que proporciona el provider.
.

function App() {
  return (
    <>
      <HashRouter>
        <AuthProvider>
          <Menu />

          <Routes>
            <Route path="/" element={<HomePage />} />

            <Route path="/blog" element={<BlogPage />}>
              <Route path=":slug" element={<BlogPost />} />
            </Route>

            <Route path="/login" element={<LoginPage />} />
            <Route path="/logout" element={<LogoutPage />} />
            <Route path="/profile" element={<ProfilePage />} />

            <Route path="*" element={<p>Not found</p>} />
          </Routes>
        </AuthProvider>
      </HashRouter>
    </>
  );
}

.
Posteriormente, creamos o modificamos los componentes necesarios para este flujo, empezando por LoginPage, LogoutPage y Profile.
.
En LoginPage lo que podemos observar es que tenemos un pequeño formulario que nos permite ingresar el username, y mediante el react hook useAuth al realizar el evento de submit utilizaremos la función login que a su vez llama a auth.login para setear el valor del nombre de usuario y redirigirnos a la página de Profile de este.
.

import React from 'react';
import { useAuth } from './auth';

function LoginPage() {
  const auth = useAuth();
  const [username, setUsername] = React.useState('');
  
  const login = (e) => {
    e.preventDefault();
    auth.login({ username });
  };
  
  return (
    <>
      <h1>Login</h1>

      <form onSubmit={login}>
        <label>Escribe tu nombre de usuario:</label>
        <input
          value={username}
          onChange={e => setUsername(e.target.value)}
        />

        <button type="submit">Entrar</button>
      </form>
    </>
  );
}

export { LoginPage };

.
En LogoutPage también se tiene un pequeño formulario que al realizar el evento de submit en este caso va a setear el estado del usuario en null y redirigirnos a la página principal / por medio del react hook useAuth y la función logout que a su vez utiliza a auth.logout.
.

import React from 'react';
import { useAuth } from './auth';

function LogoutPage() {
  const auth = useAuth();
  
  const logout = (e) => {
    e.preventDefault();
    auth.logout();
  };
  
  return (
    <>
      <h1>Logout</h1>

      <form onSubmit={logout}>
        <label>¿Segura de que quieras salir?</label>

        <button type="submit">Salir</button>
      </form>
    </>
  );
}

export { LogoutPage };

.
Finalmente para completar esta parte del flujo tenemos a ProfilePage que va renderizar la página de perfil de usuario. Este utiliza al rect hook useAuth para recuperar la información del nombre de usuario y poder renderizarlo.
.
Sin embargo dará error si no se ha pasado a través de las etapas del flujo adecuadamente. Por lo que aún se debe implementar una forma de proteger las rutas para evitar este tipo de error, o condicionar el renderizado de forma que si el usuario es null entonces manejar el renderizado de otra manera.
.

import React from 'react';
import { useAuth } from './auth';

function ProfilePage() {
  const auth = useAuth();
  
  return (
    <>
      <h1>Perfil</h1>
      <p>Welcome, {auth.user.username}</p>
    </>
  );
}

export { ProfilePage };

useAuth: login y logout


  • Definiendo rutas de Login y Logout.

    <Route path="/login" element={<LoginPage />} />
    <Route path="/logout" element={<LogoutPage />} />
    
  • Creando componentes LoginPage y LogoutPage.

    import React from "react";
    
    function LoginPage() {
      return <h1>Login</h1>;
    }
    
    export { LoginPage };
    
    import React from "react";
    
    function LogoutPage() {
      return <h1>Logout</h1>;
    }
    
    export { LogoutPage };
    
  • Agregar Links para login y logout al menú.

    import React from "react";
    import { NavLink } from "react-router-dom";
    
    function Menu() {
      return (
        <nav>
          <ul>
            {routes.map(route => (
              <li key={route.to}>
                <NavLink 
                  style={({ isActive }) => ({
                    color: isActive ? 'red' : 'blue'
                  })}
                  to={route.to}
                >
                  {route.text}
                </NavLink>
              </li>
            ))}
          </ul>
        </nav>
      )
    }
    
    const routes = [
      { to: '/', text: 'Home' },
      { to: '/blog', text: 'Blog' },
      { to: '/profile', text: 'Profile' }, 
      **{ to: '/login', text: 'Login' },
      { to: '/logout', text: 'Logout' }**
    ];
    
    export { Menu };
    
  • Creando un formulario para el login.

    import React from "react";
    
    function LoginPage() {
      **const [username, setUsername] = React.useState('');
    
      const login = (e) => {
        e.preventDefault();
        console.log(username);
      }**
    
      return (
        <>
          **<h1>Login</h1>
          
          <form onSubmit={login}>
            <label>Escribe tu nombre de usuario:</label>
            <input
              value={username}
              onChange={e => setUsername(e.target.value)}
            />
    
            <button type="submit">Entrar</button>
          </form>**
        </>
      );
    }
    
    export { LoginPage };
    
  • Creando un formulario para logout.

    import React from "react";
    
    function LogoutPage() {
    
      **const logout = (e) => {
        e.preventDefault();
        console.log('Logout');
      }**
    
      return (
        **<>
          <h1>Logout</h1>
    
          <form onSubmit={logout}>
            <label>¿Seguro de que quieres salir?</label>
    
            <button type="submit">Salir</button>
          </form>
        </>**
      );
    }
    
    export { LogoutPage };
    
  • Creamos un provider y un hook para la autentificación.

    **import React from "react";
    
    const AuthContext = React.createContext()
    
    function AuthPrvider({ children }) {
        const auth = {}
    
        return (
            <AuthContext.Provider value={auth}>
                { children }
            </AuthContext.Provider>
        )
    }
    
    function useAuth() {
        const auth = React.useContext(AuthContext)
        return auth;
    }
    
    export {
        AuthProvider,
        useAuth
    }**
    
  • Implementar el provider en la aplicación.

    import "./App.css";
    // more imports...
    **import { AuthProvider } from "./api/auth";**
    
    function App() {
      return (
        <>
          <HashRouter>
            **<AuthProvider>**
              <Menu />
    
              <Routes>
                <Route path="/" element={<HomePage />} />
    
                {/* More routes... */}
              </Routes>
            **</AuthProvider>**
          </HashRouter>
        </>
      );
    }
    
    export default App;
    
  • Creamos los métodos de login y logout en el provider y lo consumimos en LoginPage y LogoutPage respectivamente.

    ....
    
    const AuthContext = React.createContext()
    
    function AuthProvider({ children }) {
        **const navigate = useNavigate()
        const [user, setUser] = useState()
    
        const login = ({ username }) => {
            setUser({ username })
            navigate('/profile')
        }
    
        const logout = () => {
            setUser(null);
            navigate('/')
        }
    
        const auth = { user, login, logout };**
    
        return (
            <AuthContext.Provider value={auth}>
                { children }
            </AuthContext.Provider>
        )
    }
    
    ...
    
    import React from "react"
    import { useAuth } from "../api/auth"
    
    function LoginPage() {
      **const auth = useAuth()**
      const [username, setUsername] = React.useState('');
    
      const login = (e) => {
        e.preventDefault()
        **auth.login({ username })**
      }
    
      return (
        <>
          <h1>Login</h1>
          
          <form onSubmit={login}>
            <label>Escribe tu nombre de usuario:</label>
            <input
              value={username}
              onChange={e => setUsername(e.target.value)}
            />
    
            <button type="submit">Entrar</button>
          </form>
        </>
      );
    }
    
    export { LoginPage };
    
    import React from "react";
    import { useAuth } from "../api/auth";
    
    function LogoutPage() {
      **const auth = useAuth()**
    
      const logout = (e) => {
        e.preventDefault();
        **auth.logout()**
      }
    
      return (
        <>
          <h1>Logout</h1>
    
          <form onSubmit={logout}>
            <label>¿Seguro de que quieres salir?</label>
    
            <button type="submit">Salir</button>
          </form>
        </>
      );
    }
    
    export { LogoutPage };
    
  • Modificamos ProfilePage para mostrar el nombre del usuario logeado.

    import React from "react";
    import { useAuth } from "../api/auth";
    
    function ProfilePage() {
      **const auth = useAuth()**
    
      return (
        **<>
          <h1>Perfil</h1>
          <p>Welcome, {auth.user.username}</p>
        </>**
      );
    }
    
    export { ProfilePage };
    

<aside>
💡 En la siguiente clase manejaremos las rutas que seran privadas, es decir las que necesitarán autentificación para ingresar; y las rutas públicas, que serán las de autentificación.

</aside>

**English version branch and commit** <https://github.com/SebaMat3/react-router-course-p1/tree/feat/auth-context-setup> **Branch name:** feat/auth-context-setup **Commit message:** git commit -m "feat: implement initial authentication context \- Add AuthContext and AuthProvider components \- Set up basic authentication state management \- Implement useAuth custom hook for auth state access \- Add initial login/logout functionality structure"
Por si les interesa trabajar con Typescript en este proyecto, aquí esta el código adaptado para ese lenguaje (muy similar a JS pero con tipado mas estricto): ```js //auth.tsx import React, { createContext, ReactNode, useContext, useState } from 'react'; import { useNavigate } from 'react-router-dom'; // aqui se define que tipo de dato es children type AuthProviderProps = { children: ReactNode; } // aqui se definen los tipos de datos que reciben las funciones y useState de Auth Provider type AuthContextType = { // el tipo de dato es un objeto con un tipo string o un valor nulo user: {username: string} | null; // login recibe una funcion funcion con "credenciales" que son un objeto con un dato tipo string login: (credentials: { username: string }) => void; // logout recibe un valor del tipo funcion logout: () => void; } // aqui dice que createContext puede recibr los tupos definidos en AuthContextType o puede ser indefinido, y por default lo haces indefinido const AuthContext = createContext<AuthContextType | undefined>(undefined); // React FC ayuda en typescript a definir un Function Component y permite pasar props prededifinas, en este caso es necesario para definir a "children" const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => { const navigate = useNavigate(); const [ user, setUser ] = useState<{username: string} | null>(null); const login = (credentials: { username: string }) => { setUser({ username: credentials.username }) navigate('/profile'); } const logout = () => { setUser(null) navigate('/'); } const auth = { user, login, logout } return( <AuthContext.Provider value={auth}> {children} </AuthContext.Provider> ) } const useAuth = () => { const auth = useContext(AuthContext); return auth; } export { AuthProvider, useAuth } // LoginPage.tsx import React, { useState } from 'react' import { useAuth } from './hooks/auth' // React FC ayuda en typescript a definir un Function Component y permite pasar props prededifinas, en este caso no es necesario usar React.FC const LoginPage: React.FC = () => { const auth = useAuth(); const [userName, setUserName] = useState<string>('') const login = ( e:any ) => { // en este caso defini que e o event puede tener cualquier tipo de valor o any e.preventDefault(); // aqui auth? puede tener un valor o puede ser null y te permite renderizar el componente reciba o no informacion ya que es un comportamiento esperado por TS auth?.login({ username: userName }) } return( <>

LoginPage

<form onSubmit={login}> <label> Nombre/ Usuario </label> <input type='text' value={userName} onChange={ e => setUserName(e.target.value)} /> <button type='submit'> Ir </button> </form> ) } export { LoginPage } // ProfilePage.tsx import React from 'react' import { useAuth } from './hooks/auth' const ProfilePage: React.FC = () => { const auth = useAuth(); return( <>

Welcome to ProfilePage {auth?.user?.username}

{/* aqui dices que auth? y user? puede ser undefined y por eso puedes renderizar el componente aunque no reciba valores de useAuth */} ) } export { ProfilePage } // LogoutPage.tsx import React from 'react' import { useAuth } from './hooks/auth' // React FC ayuda en typescript a definir un Function Component y permite pasar props prededifinas, en este caso no es necesario usar React.FC const LogoutPage: React.FC = () => { const auth = useAuth(); const logout = (e:any) => { // en este caso defini que e o event puede tener cualquier tipo de valor o any e.preventDefault(); // aqui auth? puede tener un valor o puede ser null y te permite renderizar el componente reciba o no informacion ya que es un comportamiento esperado por TS auth?.logout(); } return( <>

LogoutPAge

<form onSubmit={logout}> <label> Da click para salir </label> <button type='submit'> Salir </button> </form> ) } export { LogoutPage } // Аpp.tsx import { Menu } from './pages/components/Menux' import { HashRouter, Routes, Route } from 'react-router-dom' import { HomePage } from './pages/HomePage' import { BlogPage } from './pages/BlogPage' import { ProfilePage } from './pages/ProfilePage' import { BlogPost } from './pages/components/BlogPost' import { LoginPage } from './pages/LoginPage' import { LogoutPage } from './pages/LogoutPage' import { AuthProvider } from './pages/hooks/auth' // recuerda que HashRouter es un provider como funciona con React Context // queremos que el auth provider este dentro, porque es muy probable que necesite llamar metodos y hooks de hashprovider function App() { return ( <> <HashRouter> {/* debe de aparecer en todas las paginas, muy recomendable que este dentro de hash router */} <AuthProvider> <Menu /> <Routes> <Route path='/' element={<HomePage />} /> <Route path='/blog' element={<BlogPage />} > <Route path=':slug' element={<BlogPost />} /> </Route> <Route path='/login' element={<LoginPage />} /> <Route path='/logout' element={<LogoutPage />} /> <Route path='/profile' element={<ProfilePage />} /> <Route path='*' element={<p> Not found

} /> </Routes> </AuthProvider> </HashRouter> ) } export default App ```
Después de 3 veces que vi la clase pude entender bien todo el proceso de Login y Logout, gracias teacher!!!

Discriminar no siempre es malo, sacar a Freddy de estos permisos es una buena manera de trabajo, ya que, cada quien tiene una chamba y autonomía.

En otras palabras, estos cursos discriminan información. Así es, información que no es relevante o que es falsa o de baja calidad.

Ya que actualmente rect-router está en la versión 6.8 he estado usando las nuevas características.
Así fue como acomodé las rutas:

const router = createHashRouter([
  { 
    element: <AuthProvider/>,
    children:[
      {  
        path: "/",
        element:<Layout/>,
        errorElement: <ErrorPage/>,
        children:[  
          {
            index:true,
            path: "/",
            element:<HomePage className="container"/>,
          },
          {
            path: "/login",
            element:<LonginPage/>,
          },
          {
            path: "/logout",
            element:<LogoutPage/>,
          },
          {
            path: "/profile",
            element:<ProfilePage/>,
          },
          {
            path: "/blog",
            element:<BlogPage/>,
            loader:postLoader,

          },
          {
            path: "/blog-post/:slug",
            element:<BlogPost/>,
            loader:loaderBlogPost,  
          },
        ]
      } 
    ]
  },
])

function App() {
  
  return (
    <React.Fragment>
      <RouterProvider router={router}/>
    </React.Fragment>

)
}

<AuthProvider/> Tiene como hijos al resto de las rutas para poder compartir el contexto. También tengo un componente Layout el cual uso como base agregar tanto el menú como las rutas hijas que se rendericen dentro de él dependiente de la url.

Así se ve el componente <AuthProvider/>:

import React from "react";
import { Outlet, useNavigate } from "react-router-dom";



const AuthContext = React.createContext();

function AuthProvider(){
    const [username, setUsername] = React.useState(null);
    const navigate = useNavigate();

    const login = ()=>{
        navigate("/profile");
    }
    const logout = ()=>{
        navigate("/");
    }
    
    const auth = {username, login, logout};
    return(
        <AuthContext.Provider value={auth}>
            <Outlet></Outlet>
        </AuthContext.Provider>
    )
}

function useAuth(){
    const data = React.useContext(AuthContext);
    return data
}

export {AuthProvider, useAuth}

Haciendo uso de un componete especial de react-router llamado Outlet; se puede traer a los hijos(childrens) especificados en las rutas.

Hasta el momento funcionó todo el ejercicio de esta clase.