No tienes acceso a esta clase

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

Reto: UX de login y logout

16/30
Recursos

Aportes 27

Preguntas 3

Ordenar por:

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

Para resolver el segundo reto utilizando location lo que debemos hacer (o al menos eso entendí) es:

  1. Dentro de nuestro <AuthRoute> llamar al useNavigate
const location = useLocation();

como AuthRoute envuelve a nuestras rutas protegidas location.pathname tomara el valor de la ruta a la que intentamos entrar antes del redirect a LoginPage.

  1. Lo siguiente es guardar este valor dentro de la propiedad state
Navigate to='/login' state={{ from: location }} replace />

Debemos colocar el atributo replace el cual nos permitirá hacer el redirect.

  1. Ahora debemos modificar nuestro método login dentro del AuthProvider para ello debemos llamar al hook useLocation tal como en el paso uno y crear una variable from que nos ayudara a apuntar al valor guardado en location del AuthRoute
let from = location.state?.from?.pathname || -1;

aqui le decimos que from es de existir (solo existe este valor si fuimos redirigiros desde una ruta protegida) igual al valor guardado en el Navigate del AuthRoute sino sera igual a la ultima ruta no protegida en la que estuvo el usuario (en el ejemplo del recurso se establece que sea siempre el home “/”).

  1. ahora solo nos queda modificar el método navigate para que nos rediría a la dirección del from en lugar de profile.
navigate(from, { replace: true })

notese que como segundo parametro pasamos el replace a true.
esto nos ayuda a que no nos rediría al perfil como establece el método login de LoginPage sino que nos lleve a la ruta del from de nuestro AuthProvider.

Si hay algo de lo que haya escrito que este errado por favor déjamelo saber con tu comentario igual si te fue de ayuda.

location.state

Investigué sobre el hook useLocation y noté que en el objeto que devuelve hay una propiedad state.
Esa propiedad state puede ser manipulada desde varias funciones o componentes, como por ejemplo:

const location = useLocation()
const navigate = useNavigate()
navigate("/login", { 
	state: 
		{ prevPath: location.pathname } 
	}

ó…

<Navigate to="/login" state={ { prevPath: location.pathname } }

.
Esta propiedad state la recibo desde mi función de login, y cuando el usuario se loggea, verifico si viene desde una redirección que setteó un state con prevPath!

const login = (username, role) => {
		setUser({ username, role });

		if (state) {
			navigate(state.prevPath);
			delete state.prevPath;
			return;
		}
		navigate("/profile");
	};

Esa fue mi solución!

Espero haberme dado a entender

Yo use navigate(-1) cuando se dispara la funcion del login, fue lo mas sencillo que encontre para mi codigo 😄

<code> 
import React from 'react’
import { useNavigate } from ‘react-router-dom’

export const Login = ({ name, setName, login, setLogin}) => {

const navigate = useNavigate()

const onLogin = (e) => {
e.preventDefault()
setLogin(!login)
navigate(-1)
}

return (
<>
<h3>Inserte su nombre</h3>
<form onSubmit={onLogin}>
<label>Nombre</label>
<input type=“text” value={name} onChange={e => setName(e.target.value)} />
<button type=“submit”>Login</button>
</form>
</>
)
}
bueno mi solución fue usar un estado inicializado en un arreglo vacío en el auth.jsx, allí mismo en la función login verifico si el arreglo está vacío después de hacer login lo redirijo a home, si el arreglo no está vacío hago un window.history.back() que me lleva a la última página vista. usando el use context llamo ese estado en cada página y con un useEffect cada vez que se renderice una página pongo dentro del arreglo el location.href. es decir si la persona llega directamente al login el arreglo está vacío y lo envía al home, si la persona llega a home y ver por ejemplo la página de blog y de allí pasa a loguearse el arreglo ya no está vacío y lo envía a la última página vista que en este caso sería blog!!!

Una posible solución al reto usando Redux

Cuado se ingrese a la ruta privada, voy a guardar la ruta en el estado desde location.pathname, al usuario lo redirecciono al login como se hizo en clase, pero cuando haga el login exitosamente valido si en el estado hay una ruta guardada, para saber que pagina le muestro.
Para finalizar, para borrar el estado, valido si el usuario se encuentra en la misma ruta que se guardo la primera vez, y borro el estado.

Repositorio en GitHub

Link del proyecto desplegado

  • auth-router.jsx
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import { Navigate, useLocation } from 'react-router-dom';

import { setLastUrlAction } from '@modules/nav/actions';
import { getAuth, isAuthSuccess } from '@modules/auth/selectors';
import { getLastUrl } from '@modules/nav/selectors';

const AuthRoute = ({ children }) => {
  const dispatch = useDispatch();
  const location = useLocation();

  const auth = useSelector(getAuth);
  const isSuccess = useSelector(isAuthSuccess);
  const lastUrl = useSelector(getLastUrl);

  if (!auth?.email && !isSuccess && !lastUrl) {
    dispatch(setLastUrlAction(location.pathname));
    return <Navigate to="/login" />;
  }

  useEffect(() => {
    if (location.pathname === lastUrl) {
      dispatch(setLastUrlAction(undefined));
    }
  }, []);

  return children;
};

AuthRoute.propTypes = {
  children: PropTypes.element.isRequired,
};

export default AuthRoute;

  • login.jsx
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';

import { loginAction } from '@modules/auth/actions';
import {
  getAuth,
  isAuthSuccess,
  getAuthErrorDetail,
  isAuthError,
  isAuthLoading,
} from '@modules/auth/selectors';
import { getLastUrl } from '@modules/nav/selectors';

import Loader from '@components/loader';
import Error from '@components/error';

import Button from '@ui/button';
import Input from '@ui/input';

import './login.scss';

const Login = () => {
  const navigate = useNavigate();
  const dispatch = useDispatch();

  const auth = useSelector(getAuth);
  const isSuccess = useSelector(isAuthSuccess);
  const isLoading = useSelector(isAuthLoading);
  const isError = useSelector(isAuthError);
  const errorDetail = useSelector(getAuthErrorDetail);

  const lastUrl = useSelector(getLastUrl);

  const [email, setEmail] = useState('');

  const handleLogin = (event) => {
    dispatch(loginAction({ email }));
    event.preventDefault();
  };

  useEffect(() => {
    if (isSuccess && auth?.email) {
      if (lastUrl) {
        navigate(lastUrl);
      } else {
        navigate('/');
      }
    }
  }, [auth, isSuccess, lastUrl]);

  return (
    <section className="Login">
      <div className="Login__container">
        <h1>Inicio de sesión</h1>
        <form onSubmit={handleLogin}>
          <Input
            value={email}
            onChange={(event) => setEmail(event.target.value)}
          />
          <Button primary type="submit" onClick={() => {}}>
            Iniciar sesión
          </Button>
        </form>
      </div>
      {isLoading && <Loader />}
      {isError && <Error error={errorDetail} />}
    </section>
  );
};

export default Login;

  • actions.js
import { SET_SEARCH, SET_LAST_URL } from './types';

export const setSearchAction = (payload) => ({
  type: SET_SEARCH,
  payload,
});

export const setLastUrlAction = (payload) => ({
  type: SET_LAST_URL,
  payload,
});

  • reducer.js
import { SET_SEARCH, SET_LAST_URL } from './types';

export const initialState = {
  search: undefined,
  lastUrl: undefined,
};

// eslint-disable-next-line default-param-last
const navReducer = (state = initialState, action) => {
  switch (action.type) {
    case SET_SEARCH: {
      return {
        ...state,
        search: action.payload,
      };
    }
    case SET_LAST_URL: {
      return {
        ...state,
        lastUrl: action.payload,
      };
    }
    default:
      return state;
  }
};

export default navReducer;

  • selectors.js
import { createSelector } from '@reduxjs/toolkit';

export const selectNav = (state) => state.nav;

export const getSearch = createSelector(selectNav, (auth) => auth.search);
export const getLastUrl = createSelector(selectNav, (auth) => auth.lastUrl);

Esta es mi solución usando useLocation y useRef

Costó, pero está terminado.

Cambios en auth.js

// ...
  const login = ({ username, locationAfterLogin }) => {
        const isModerator = moderators.find(mod => mod === username)
        const isSpellChecker = spellCheckers.find(sp => sp === username)
        const isEditor = editors.find(ed => ed === username)

        setUser({ username, isModerator, isEditor, isSpellChecker })
        navigate(locationAfterLogin)
    }
// ...
export function AuthRoute({ children }) {
    const auth = useAuth()
    const { pathname } = useLocation()

    if (!auth.user)
        return <Navigate to="/login" state={{ locationAfterLogin: pathname }} />
    return children
}
// ...

Cambios en LoginPage.js

// ...
  const { state } = useLocation()
    let locationAfterLogin
    state
        ? (locationAfterLogin = state.locationAfterLogin)
        : (locationAfterLogin = '/profile')

    if (auth.user) return <Navigate to="/profile" replace />

    const login = e => {
        e.preventDefault()
        auth.login({ username, locationAfterLogin })
    }
// ...

Yo simplemente agrego

navigate(-1)

A la ultima linea de la funcion login() para redirigir a la ultima vista en lugar del profile.

Aca dejo el reto:

Pero antes de verlo te reto a pienses un rato. Yo te doy otra pista. Tenes que usar el estado de la navegacion.

=====

=====

=====

=====

=====

=====

// App.js
<Route path="/" element={<RequireAuth />}>
	<Route index element={<Home />} />
	{/* Mis rutas */}
</Route>
<Route path="auth" element={<Unauthenticated />}>
	<Route path="login" element={<Login />} />
	{/* Mis rutas signup, login, forgotPassword, etc*/}
</Route>
// RequireAuth.js
import { useLocation, Navigate, Outlet } from 'react-router-dom'
const RequireAuth = () => {
  const location = useLocation()
  return isAuthenticated ? (
    <Layout>
       <Outlet />
    </Layout>
    : (
      <Navigate to="/auth/login" state={{ from: location }}  />
    )
}

export default RequireAuth
// Unauthenticated.js
import { Outlet, useLocation } from 'react-router-dom'
const location = useLocation()
const from = location.state?.from?.pathname || '/'
return isAuthenticated ? (
    <Navigate to={from} replace />
  ) : (
    <Outlet />
  )

Como me coste este reto, gracias a la comunidad pude lograrlo la verdad

yo lo resolví solo haciendo modificaciones en el authProvider usando el useLocation, haciendo push de cada ruta por la que paso en un array usando React.useState, y luego vacío el array despues de hacer login o logout para que no se llene mucho ```js function AuthProvider({ children }) { const navigate = useNavigate(); const location = useLocation(); const [user, setUser] = React.useState(null); const [locations,setLocations]= React.useState([]) React.useEffect(()=>{ setLocations((prev)=>[...prev,location]) },[location]) const login = ({ username }) => { const isAdmin = adminList.find(admin => admin === username); const lastLocation=locations.slice(-2)[0].pathname setUser({ username, isAdmin }); console.log(lastLocation); (!locations.slice(-2)[0].pathname || 'login')?navigate('/profile'):navigate(lastLocation); setLocations([]) }; const logout = () => { setUser(null); navigate('/'); setLocations([]) }; ``` function AuthProvider({ children }) {  const navigate = useNavigate();  const location = useLocation();   const \[user, setUser] = React.useState(null);  const \[locations,setLocations]= React.useState(\[])   React.useEffect(()=>{    setLocations((prev)=>\[...prev,location])  },\[location])   const login = ({ username }) => {    const isAdmin = adminList.find(admin => admin === username);    setUser({ username, isAdmin });    navigate(locations.slice(-2)\[0].pathname);    setLocations(\[])  };    const logout = () => {    setUser(null);    navigate('/');    setLocations(\[])   }; 

mi solucion fue esta , obvio hay mas codigo para manejar los otros estados pero para el reto de esta clase creo que fue lo mas conveniente

const onLogin = () => {
const historial = window.history
if(historial.length>3){navigate(-2)}
navigate(’/profile’)
}

Redirrecion a la pagina solicitada originalmente

1.) Privatizamos al componente EDIT con nuestro AuthRouter:
App.js

 <Route path="/blog/:slug/edit/:id" element{<AuthRouter><Edit /></AuthRouter>} />

2.) Modificamos “AuthRouter”:

auth.js

const AuthRouter = ({ children }) => {
  const auth = useAuth();
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    if (!auth.user.name) {
      navigate("/login", {
        state: {
          prev: location.pathname,
        },
      });
    }
  }, []);
  return children;
};

Nuestra propiedad state se activa solo cuando existe una redireccion y transmite algun valor entre los componentes involucrados.
En nuestro caso, si el usuario tiene la url directa a /profile o /edit/:id, lo redireccionara a “/login” pero en state guardaremos la url de donde empezamos a navegar.

3.) Ahora, vamos al componente receptor y mostremos en consola los datos que trajo de la anterior url:

Login.js

  const location = useLocation();

  useEffect(() => {
    console.log(location);
  });

4.) Por ultimo, cuando el usuario termine su registro:

Login.js

  function handleSubmit(event) {
    event.preventDefault();
    auth.login(user);
    navigate(location.state?.prev || "/");
  }

En mi caso, el state lo llame como prev y guarda una url (de donde nos redireccionaron). Un default value “/” en caso de no haber redireccion.

Comentarios y sugerencias bienvenidas.

Código del reto en TS:
Pensé en utilizar el context de auth para transferir esa información pero viendo los comentarios vi que react router ya trabaja con variables y pues una gran oportunidad para usarlo:

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

interface User {
    username: string,
    isAdmin: boolean,
}

interface Auth {
    user: User | null
    login: (value: Omit<User, "isAdmin">) => void
    logout: () => void,

}

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

const adminList = ['Max', 'Emma', 'Aleksei'];

const AuthContext = createContext({} as Auth);

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

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

    const login: Auth['login'] = ({ username }) => {
        const isAdmin = adminList.some(admin => admin === username);
        setUser({ username, isAdmin });
        navigate( state.from || '/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 }
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "./useAuth"
import { FC, PropsWithChildren } from "react";

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

    const auth = useAuth();
    const { pathname } = useLocation();

    if(!auth.user) return <Navigate to="/login" state={{
        from: pathname
    }} />

    return  children;
}

export { AuthRoute }

Analizando useLocation(), se me ocurre que puede ser muy útil para pasar información de una ruta a otra, mediante location.state, cuando navegamos con el componente Navigate o con el hook useNavigate(), por ejemplo, un escenario de formulario paso a paso, donde cada paso, depende de la información del paso anterior.

Lo intente bro pero no pude GG
Mas adelante lo intentare solo estuve 3 horas intentando seguro maña lo hare, no me eh rendido solo lo hare después.

Para resolver el reto usé un useLocation y el buen useEffect. En mi caso tengo un custom hook llamado useAuth.jsx. Dentro del hook cree un estado para actualizar la localización dependiendo de donde esté el usuario (/home, /blog /blopost/:plug).

en el return mando la función upDateLocation y todos estos datos los recibo en mi componente <Layout> el cual contiene las rutas hijas con un <Outlet> (excepto el <Menu> que está por fuera)

Gracias al context que admite <Outlet> estoy compartiendo los datos com todos los demás componentes. La idea ahora es actualizar con setLocation dependiendo en que ruta estuviere el usuario de esta manera:

Aquí estoy en el componente <BlogPage> e intento actualizar el estado location que está en mi custom hook useAuth enviando por parámetro la ruta con el location.pathname que me ofrece useLocation(). Pero obtengo el siguiente error.

Dice que no puedo actualizar al componente layout mientras trato de renderizar por primera vez al componente blogpage. Espero se pueda apreciar en la foto.
Entonces lo pude corregir de esta manera:

Uso un useEffect que se active siempre luego de renderizarse mi componente blogpage y con eso no tuve más ese error. Para terminar así es como desde el componente <LoginPage> hago la navegación

Le mando como parámetro a navigate el estado actual con la última ruta visitada por el usuario. De esta manera usé el useEffect en los demás componentes y de la misma forma comparto el estado con "const [authData] = useOutletContext();". La aplicación funciona haciéndolo así pero claro que no sé si será la mejor idea. Bueno pero se logró el reto XD.

Mi solucion no es nada elegante. Elimine el componente Navigate y lo reemplace con el hook useNavigate.

function AuthRoute({ children }) {
    const auth = useAuth();
    const notAuthorized = () => {
	return auth.navigate('/login')
}
    if(!auth.user) {

        return (
            <>
            <p>Deberías iniciar sesión primero</p>
            <button
             onClick={notAuthorized}
             >Loging</button>
            </>
        )
    }
    return children
}

En la funcion login de AuthProvider cambie navigate(’/profile’)
a navigate(-1) asi:

const login = ({ username }) => {
        const staff = users.find(user => user.name === username);
        staff !== undefined ?
        setUser(staff) :
        setUser({ name: username, role: roles.visitor });
        navigate(-1);
    }

Además también quite el condicional en LoginPage

 // if(auth.user) {
    //     return <Navigate to={'/profile'}/>
       
    // }

Mi solucion fue:

  1. Declarar la variable “previousURL” al inicio.
  2. Inicializarla en la funcion “AuthRoute”.
  3. En la funcion “login”, si la variable es distinta de vacio se navega a esa pagina, en caso contrario se va a la pagina de profile
import React, { createContext, useContext, useState } from "react";
import { Navigate, useNavigate, useLocation } from "react-router-dom";
import BlogData from "./BlogData";

const adminList = ["Irisval", "RetaxMaster", "freddier"];
let previousURL = '';

const AuthContext = createContext();

function AuthProvider({ children }) {
  const navigate = useNavigate();
  const [data, setData] = useState(BlogData);
  const [user, setUser] = useState(null);

  const login = ({ username }) => {
    const isAdmin = adminList.find((admin) => admin === username);
    setUser({ username, isAdmin });

    navigate(previousURL || "/profile");
  };

  const updateBlog = (idPost, input, value) => {
    let newPost = data.find((post) => post.id === idPost);
    newPost = { ...newPost, [input]: value };

    const newData = data.map((post) => {
      if (post.id === idPost) return newPost;
      return post;
    });
    setData(newData);
  };

  const deletePost = (idPost) => {
    const newData = data.filter((post) => post.id !== idPost);
    setData(newData);
    navigate("/");
  };

  const logout = () => {
    setUser(null);
    navigate("/");
  };

  const auth = { user, login, logout, data, updateBlog, deletePost };

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

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

function AuthRoute({ children }) {
  const auth = useContext(AuthContext);
  const { pathname } = useLocation();
  previousURL = pathname;

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

  return children;
}

export { AuthProvider, useAuth, AuthRoute };
Hola, Juandc. Mi novia dice que hablas como Ebna Moda, tendrá razón?
Logre el reto pero la tardanza fue mas en encontrar los recursos necesarios. Un little tip para los profesores compartan los recursos que se van a necesitar. Por ejemplo yo sabia que tenia que usar UsLocation pero no tenia ni la remota ide que tenia que usar state y ni como se usaba y eh ahi mi tardanza. Y se que estan en los comentarios pero no queria ver la solucion.

Solución

.
Para resolver el reto vamos a hacer algunos cambios en el archivo auth.js.
.
Dentro del componente AuthRoute vamos a utilizar el hook useLocation para obtener el objeto de la ubicación actual del React Router.
.
Este objeto de ubicación contiene información sobre la URL actual, como la ruta (pathname), la búsqueda (search), y el estado de la ubicación (state).
.

function AuthRoute(props) {
  const auth = useAuth();
  const location = useLocation();

  if (!auth.user) {
    return <Navigate to="/login" state={{ prevLocation: location.pathname }} />;
  }

  return props.children;
}

.
Recordemos que en App.js lo utilizábamos por ejemplo para proteger la ruta profile; es decir, si no estás autenticado AuthRoute por medio del Navigate te hará un redirect a login para que te autentiques.
.
Es por eso que poco antes de tener el redirect a /login guardaremos el nombre de la ruta pathname como prevLocation en un objeto que se pasa como estado por medio de la prop state de Navigate.
.
De esta manera cuando ya no encontremos en /login después de que se nos haya hecho el redirect, vamos a notar que por medio de useLocation podemos acceder al state que se nos mandó desde la anterior ruta.
.

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

  const login = ({ username }) => {
    const isAdmin = adminList.find((admin) => admin === username);
    setUser({ username, isAdmin });
    navigate(location?.state?.prevLocation || "/");
  };

  const logout = () => {
    setUser(null);
    navigate("/");
  };

  const auth = { user, login, logout };

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

.
Por lo que en AuthProvider volveremos también a utilizar useLocation, y en su función login al momento de navegar evaluaremos si existe en location el state con propiedad prevLocation.
.

navigate(location?.state?.prevLocation || "/");

.
En el caso de que sí exista significa que anteriormente tuvimos un redirect a login por lo cual navegaremos hacia el prevLocation que indica la ruta desde la que se nos hizo redirect a login.
.
Mientras que si no existe prevLocation significa que accedimos a login por voluntad propia, por lo que navegaremos al home / por defecto.
.
El código final de auth.js quedaría de esta manera:
.

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

const adminList = ["Irisval", "RetaxMaster", "freddier"];

const AuthContext = React.createContext();

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

  const login = ({ username }) => {
    const isAdmin = adminList.find((admin) => admin === username);
    setUser({ username, isAdmin });
    navigate(location?.state?.prevLocation || "/");
  };

  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;
}

function AuthRoute(props) {
  const auth = useAuth();
  const location = useLocation();

  if (!auth.user) {
    return <Navigate to="/login" state={{ prevLocation: location.pathname }} />;
  }

  return props.children;
}

export { AuthProvider, AuthRoute, useAuth };

.
De esta manera si desde la url pusiéramos la ruta /profile u cualquier otra ruta que requiera estar autenticados, se nos hará un redirect a /login; después de autenticarnos navegaremos automáticamente hacia la ruta desde las que se nos hizo el redirect a login, en este caso hacia /profile.
.
Finalmente si accedemos normalmente al login por nuestra cuenta sin un redirect, entonces tras autenticarnos navegaremos por defecto hacia el home /.

Reto: UX de login y logout

.
Si vemos a nivel de User Experience resulta que cuando nosotros hacemos los redirects, cuando por ejemplo entramos a login pero ya estaba logueado, o cuando entramos a profile no nos hemos autenticado y hacemos el login. Todos esos sistemas funcionan muy bien; es decir, hacen un redirect, nos impiden y protegen nuestras rutas donde tenemos rutas privadas, protegidas, etc.
.
Qué pasa luego si un usuario entra en algún otro sitio, como por ejemplo a editar un blogpost; pero pasa que no estaba autenticado, entonces se me hizo redirect a la página de login, me autentico y me mandan a /profile, a mi perfil.
.
Esa no es la mejor User Experience, porque en realidad yo estaba intentando entrar a una url muy específica, a la de editar un blogpost en particular, una url en específico de la que me hicieron redirect a login porque no estaba autenticado. Pero después de que me autentique yo esperaría que la aplicación me devuelva a esa parte de la aplicación o url a la que no nos dejó entrar originalmente. Sin embargo, se nos hizo redirect a profile, cuando no queríamos ver nuestro perfíl, sino se quería editar ese blogpost u elemento en particular.
.
El reto es mejorar la UX de nuestra aplicación y hacer que siempre se haga redirect después del login a la página desde la cual nos hicieron redirect al formulario para autenticación, que hagamos redirect a esa última página la que nos mandó al login.
.
Se aconseja utilizar useLocation para hacer un redirect hacia otro sitio, dependiendo si eso es lo que el usuario quiere, y sino si solo hacemos login entonces que nos mande a profile cmo acción por defecto.

Reto completado * Lo que hice es que en mi provider establezco el valor por default del login que es /profile, este valor cuando lo redirecciono cambia mediante el location.hash haciendo que cuando ingrese a la pagina lo mande directamente donde estaba, y cuando el usuario sale de la pagina establezco el valor a /profile ![](https://static.platzi.com/media/user_upload/image-73c83905-2c9a-419a-afb5-af52fa5c12d1.jpg)![](https://static.platzi.com/media/user_upload/image-a37c4cd0-8374-4917-b27a-2a70a2719607.jpg)![](https://static.platzi.com/media/user_upload/image-d591236b-21d0-4ec6-ba7a-fc681eb7cce7.jpg)![](https://static.platzi.com/media/user_upload/image-3faa7ba9-e314-44c1-b0dc-2c7092e771e2.jpg) * ![](https://static.platzi.com/media/user_upload/image-2cf7274b-3050-4b31-b67b-26b850e74d4c.jpg)

Como cuando esperas que el Profe escriba algo en los comentarios y nada 🙈

basicamente hice esto:

en LoginPage.js
enviar el state a la funcion login

const { state } = useLocation();
const login = (event) => {
    event.preventDefault();
    auth.login({ username, state });
  };

en auth.js
agregar un parametro que espera el state y hace el redirect de donde lo llamaron

const login = ({ username, state }) => {
    const isAdmin = users.find(
      (user) => user.username === username && user.rol === roles.admin
    )
      ? true
      : false;

    setUser({ username, isAdmin });

    if (state) {
      navigate(state.prevPath);
      delete state.prevPath;
      return;
    }
    navigate("/profile");
  };

y un ejemplo de un componente donde llama a login
donde envio de donde lo estoy llamando

const location = useLocation();

const login = () => {
    navigate("/login", {
      state: { prevPath: location.pathname },
    });
  };

My solution:

BlogPost.js

import React, { useContext } from "react";
import { useParams, useNavigate, useLocation } from "react-router-dom";

import { useAuth } from "./Context/auth";
import { DataContext } from "./Context/DataContext";

const BlogPost = () => {
  const navigate = useNavigate();
  const { slug } = useParams();
  const auth = useAuth();
  const { createPost, readPost, updatePost, deletePost } =
    useContext(DataContext);
  const location = useLocation();
  const blogPost = readPost(slug);

  const returnToBlog = () => {
    navigate("/blog");
  };
  const canCreate = auth.user;

  const canUpdate =
    auth.user?.isAdmin || blogPost.author === auth.user?.username;

  const canDelete =
    auth.user?.isAdmin || blogPost.author === auth.user?.username;

  const addNewPost = () => {
    createPost({
      title: "korn",
      slug: "korn",
      content: "cool",
      author: auth.user?.username ?? "unknow",
    });
  };

  const updateCurrentPost = () => {
    updatePost({
      slug,
      title: "Korn Rocks!!!",
      updatedBy: auth.user?.username ?? "unknow",
    });
  };

  const deleteCurrentPost = () => {
    deletePost(slug);
    returnToBlog();
  };

  const login = () => {
    navigate("/login", {
      state: { prevPath: location.pathname },
    });
  };

  return (
    <>
      <h2>Blog Post</h2>
      <button onClick={returnToBlog}>Volver al blog</button>
      <h3>{slug}</h3>
      <h3>{blogPost && blogPost.title}</h3>
      <p>{blogPost && blogPost.content}</p>

      {canCreate && <button onClick={addNewPost}>Agregar blogpost</button>}
      {canUpdate && (
        <button onClick={updateCurrentPost}>Actualizar blogpost</button>
      )}
      {!auth.user && (
        <button onClick={login}>Deseas Actualizar este blogpost?</button>
      )}
      {canDelete && (
        <button onClick={deleteCurrentPost}>Eliminar blogpost</button>
      )}
    </>
  );
};

export { BlogPost };

auth.js

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

import { users, roles } from "../blogdata";

const AuthContext = React.createContext();

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

  const login = ({ username, state }) => {
    const isAdmin = users.find(
      (user) => user.username === username && user.rol === roles.admin
    )
      ? true
      : false;

    setUser({ username, isAdmin });

    if (state) {
      navigate(state.prevPath);
      delete state.prevPath;
      return;
    }
    navigate("/profile");
  };

  const logout = () => {
    setUser(null);
    navigate("/login");
  };

  const auth = { user, login, logout };

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

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

function AuthRoute(props) {
  const auth = useAuth();
  const location = useLocation();
  console.log("location", location);

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

  return props.children;
}

export { AuthProvider, useAuth, AuthRoute };

LoginPage.js

import React from "react";
import { Navigate, useLocation } from "react-router-dom";

import { useAuth } from "./Context/auth";

const LoginPage = () => {
  const auth = useAuth();
  const { state } = useLocation();
  const [username, setUsername] = React.useState("");

  const login = (event) => {
    event.preventDefault();
    console.log(username);
    auth.login({ username, state });
  };

  if (auth.user) {
    return <Navigate to="/profile" />;
  }

  return (
    <>
      <h1>LoginPage</h1>
      <form onSubmit={login}>
        <label>Escribe tu nombre de usuario:</label>
        <input
          value={username}
          onChange={(event) => setUsername(event.target.value)}
        />
        <button type="submit">Entrar</button>
      </form>
    </>
  );
};

export { LoginPage };