skip to Main Content

I want the user to be redirected to the home page after successfully logging in, I store the data of the api request to log in the user in a variable called currentUser, so if currentUser is true it should redirect to home.
this is how i handle the login request :

export const AuthContextProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(
    JSON.parse(localStorage.getItem("users")) || null
  );

  const login = async (inputs) => {

  

    const res= await axios.post("https://micacarballo-social-media-api.onrender.com/api/v1/auth/login",inputs,{
      
    })
  setCurrentUser(res.data)
  };

  useEffect(() => {
    localStorage.setItem("users", JSON.stringify(currentUser));
  }, [currentUser,login]);
 
  const updateUser = async () =>{
    const user = JSON.parse(localStorage.getItem("users"))
    const res= await axios.get("https://micacarballo-social-media-api.onrender.com/api/v1/users/me/",{
 headers: {
  Authorization: `jwt ${user.token}`
 }

    })
   setCurrentUser(res.data)
  }
  return (
    <AuthContext.Provider value={{ currentUser, login , updateUser}}>
      {children}
    </AuthContext.Provider>
  );
};

My protected routes :

import { AuthContext } from './context/authContext'
function App() {
  
  const { currentUser } = useContext(AuthContext);

 

  

 const Layout = ()=>{
  

  return(
    <div  >
        <Navbar  />
        <div style={{ display: "flex" }}>
          <LeftBar />
          <div style={{ flex: 6 }}>
            <Outlet   />
          </div>
          
        </div>
      </div>
  )
 }
 
 const ProtectedRoute = ({children}) =>{
  if(!currentUser){
    return <Navigate to="/login" />
  }return children
  
}


  const router = createBrowserRouter([
   {
      path: "/",
      element: (
      <ProtectedRoute>
      <Layout/>
      
      </ProtectedRoute>
      ),
      children :[
        {
          path:"/",
          element: <Home />
        },{
          path:"/profile/:id",
          element:<Profile />
        }
      ]
    }, {
      path: "/login",
      element: <Login />,
    },
    {
      path: "/register",
      element: <Register />
    },
    
  ]);

  

  return (
    <div className="App">
       <RouterProvider router={router} />
    </div>
  )
}

export default App

and my login page :

const Login = () => {
  const [inputs, setInputs] = useState({
    email: "",
    password: "",
  });
  const [err, setErr] = useState(null);
  const [isLoading, setIsLoading] = useState(false); // state variable for loading spinner


  const navigate = useNavigate()

  const handleChange = (e) => {
    setInputs((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };
  const {login} = useContext(AuthContext)

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      setIsLoading(true); // set loading to true
      await login(inputs);
      navigate("/")
      
      
     
    } catch (err) {
      setErr(err.response.data);
    }finally {
      setIsLoading(false); // set loading back to false
    }
  };
       

    return (
      <div className='login'>
      <div className="card">
          <div className="left">
              <h1>SocialMica</h1>
              <p>
                  Make connecting with friends easy and fun
              </p>
              <span>Dont have an account yet?</span>
               <Link to="/register">
               <button>Register</button>
               </Link>
             
          </div>
          <div className="right">
              <h1>Login</h1>
              <form action="">
                <input type="email" placeholder='email' name='email' onChange={handleChange}></input>
                <input type="password"placeholder='password' name='password' onChange={handleChange}/>
               {err && "invalid email or password"}
               
               <button onClick={handleLogin} disabled={isLoading}> {isLoading ? <Loading/> : "Login"}</button>
              
               
              </form>
              </div>

      </div>
  </div> )
}

export default Login

I dont understand why its not working, after the user is logged in and currentUser stores the data, the login page just refresh and doesnt redirect,I have to manually access to the home page.
I tried changing the navigate to ("/register) in the handleSubmit function in the login page and it works properly but not with navigate(‘/’)

2

Answers


  1. change your path /home not only /
    just like

    {
        path:"/home",
        element: <Home />
    },
    

    it should working

    Login or Signup to reply.
  2. Issue

    The issue is that in the handleLogin callback the login handler enqueues a currentUser state update and in the same render cycle enqueues a navigation action. When the navigation action is immediately processed the currentUser state is yet to be updated and the AuthContextProvider hasn’t rerendered with the updated currentUser state for the UI to read. This means that ProtectedRoute reads the not-yet-updated currentUser state value and redirects back to "/login". The reason redirecting to "/register" works is because it’s not rendered on a protected route.

    Solution Suggestions

    To get around this you need to update the code to not enqueue the state update and navigation request in the same render cycle.

    • Method 1: delay the navigate call to the end of the Javascript event queue in the handleLogin handler.

      const handleLogin = async (e) => {
        e.preventDefault();
        try {
          setIsLoading(true); // set loading to true
      
          await login(inputs);
      
          // Allow state update to process, then redirect
          setTimeout(navigate, 0, "/", { replace: true });
        } catch (err) {
          setErr(err.response.data);
        } finally {
          setIsLoading(false); // set loading back to false
        }
      };
      
    • Method 2: delay the return of the login call to the end of the Javascript event queue

      const login = async (inputs) => {
        const res = await axios.post(
          "https://micacarballo-social-media-api.onrender.com/api/v1/auth/login",
          inputs,
          {}
        );
      
        // Enqueue state update
        setCurrentUser(res.data);
      
        // Delay return so state can update first
        await new Promise(resolve => {
          setTimeout(resolve, 0);
        })
      };
      

    Demo

    Edit strange-hoover-coi7hg

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search