React useState and History.push issue

im working on a react app,and I came up with the following issue: Im triyng to simulate the registration process of a new user. For that a have a funtion, called HandleRegister,it goes through the users array and validates that the new user´s name doesn’t already exists, then if everything goes well,it sholud register it and redirect to /login, if there is an error the Failed state should update, re-rendering the component and showing the div that contains an alert message The problems is the following: no matter the result of the validation, it is redirecting me to /login and adding the new user to the array here is the code for RegisterPage.js

    import { React, useState } from "react";
import { useHistory } from "react-router";
import useAuth from "../../auth/useAuth";
export default function RegisterPage() {
  const auth = useAuth();
  const [newUserName, setnewUserName] = useState("");
  const [newUserPassword, setnewUserPassword] = useState("");
  const [Failed, setFailed] = useState(false);
  const updateNewUserName = (e) => setnewUserName(e.target.value);
  const updateNewUserPassword = (e) => setnewUserPassword(e.target.value);
  const history = useHistory();
  const HandleRegister = async () => {
    var Users = auth.getUsers();
    Users.forEach(({ username }) => {
      if (username === newUserName) {
        setFailed(true);
      }
    });

    if (Failed) {
      console.log("Error!");
    } else {
      auth.setUsers(newUserName, newUserPassword);
      setnewUserName("");
      setnewUserPassword("");
      history.push("/login");
    }
  };

  return (
    <div>
      {Failed && (
        <div class="alert alert-danger" role="alert">
          This username already exist!
        </div>
      )}
      <h1>Register</h1>
      <form className="form-control">
        <label for="username" className="form-check-label">
          Usuario
        </label>
        <input
          id="username"
          placeholder="username"
          className="form-check-input"
          type="text"
          className="form-control"
          value={newUserName}
          onChange={updateNewUserName}
        ></input>
        <label for="password" className="form-check-label">
          Contraseña
        </label>
        <input
          id="password"
          placeholder="password"
          className="form-check-input"
          type="text"
          className="form-control"
          value={newUserPassword}
          onChange={updateNewUserPassword}
        ></input>
        <button
          type="button"
          className="btn btn-primary m-2"
          onClick={HandleRegister}
        >
          REGISTER
        </button>
      </form>
    </div>
  );
}

UseAuth is a hook for consuming a AuthProvider Context

AuthProvider.js code :

import { createContext,useState,useEffect,useContext } from "react";
export const AuthContext = createContext();
export const UsersContext = createContext();

const AuthProvider = ({children}) =>{
    const [user, setUser] = useState(
        JSON.parse(localStorage.getItem("user")) || null
    );
    useEffect(()=>{
        try {
            localStorage.setItem("user",JSON.stringify(user));
            
        } catch (error) {
            localStorage.removeItem("user")
            console.log(error)
        }
    },[user])
    const contextValue={
        user,
        login(user,password){setUser({username:user,password:password})} //en realidad este seria el ultimo paso,caso real consumir api => consultar => validar 
        ,
        logout(){
            setUser(null);
        }, 
        getUsers(){
            var Users= JSON.parse(localStorage.getItem("users")) || [] 
            if(!(Users instanceof Array)) Users = [Users]; 
            return Users
       },
       setUsers(newName,newPass){

        var Users = this.getUsers()
        Users.push({username:newName,password:newPass}); 
        Users = Users.filter(Boolean)
        localStorage.setItem("users", JSON.stringify(Users));

       },
       islogged(){
            return !! user;
        }
    }


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

}


export default AuthProvider;

UseAuth.js code

import {useContext} from 'react'
import { AuthContext } from './AuthProvider'

export default function useAuth() {
    const contextValue = useContext(AuthContext);
    return contextValue
}

the weird thing is a came up with a similar solution for the login validation process, that works as intended

Login.js code

import { React, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import useAuth from "../../auth/useAuth";

const LoginPage = () => {
  const auth = useAuth();
  const history = useHistory();
  const location = useLocation();
  const [User, setUser] = useState(null);
  const [Password, setPassword] = useState(null);
  const [Failed, setFailed] = useState(false);
  const updateUser = (e) => setUser(e.target.value);
  const updateUserPassword = (e) => setPassword(e.target.value);

  const handleLogin = () => {
    var Users = auth.getUsers();
    Users.forEach(async ({ username, password }) => {
      if (username === User && password === Password) {
        await auth.login(User, Password);
        setFailed(false);
        history.push(location.state?.from || "/");
      }
    });
    setFailed(true);
  };
  return (
    <div>
      {Failed && (
        <div class="alert alert-danger" role="alert">
          Wrong Username or password!
        </div>
      )}

      <div class="col-md-4 mx-auto">
        <div class="card mt-4 text-center carta">
          <div class="card-header  carta-header">
            <h1>Login</h1>
          </div>
          <div class="card-body">
            <form>
              <div class="mt-3">
                <input
                  class="input form-control"
                  type="text"
                  name="code"
                  placeholder="username"
                  value={User}
                  onChange={updateUser}
                />
              </div>
              <div class="mt-3">
                <input
                  class="input form-control"
                  type="password"
                  name="code"
                  placeholder="***"
                  value={Password}
                  onChange={updateUserPassword}
                />
              </div>
              <div class="mt-3">
                <button
                  class=" btn btn-primary tamaño"
                  type="button"
                  id="submit"
                  onClick={handleLogin}
                >
                  <span>GO</span> <i class="fa fa-check"></i>
                </button>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  );
};
export default LoginPage;

I´made some tests and if I use a regular variable (not a react state) to manage the Failure of the register process it works as it should, but i dont think thats the ideal way of dealing with this. what would be the best way to code this?

Answer

Note that useState is Asynchronous, so when you use if (username === newUserName) {setFailed(true);}, Failed will be true just after re-render the component, to avoid that, try this code :

const HandleRegister = async () => {
    const failed = false;
    var Users = auth.getUsers();
    Users.forEach(({ username }) => {
      if (username === newUserName) {
        failed = true;
      }
    });

    if (failed ) {
      console.log("Error!");
    } else {
      auth.setUsers(newUserName, newUserPassword);
      setnewUserName("");
      setnewUserPassword("");
      history.push("/login");
     //for me, I prefer in this case use history.replace instead of history.push
    }
  };