Skip to content
Advertisement

I cant get my state updated synchronously and it just performs on next render in React

I want my react states to be updated immediately as soon as I refactor the data in them so that I can run the functions in correct order, otherwise, when it performs the next function I declared, it will just execute the undefined or null data since its not yet updated.

How can I fix this issue? I know some will say that states are not made for this and have to be used asynchronously but I don’t know how I can else make that work out and let the users get logged in and redirect to their profile page only when they type correct users credentials, this might not sound a big deal for most of you but I’m just new to react and have been dealing with that for the last few days passed, basically users should be able to granted for the profile page only when they successfully log in and otherwise should get an error..

But, to get it running perfectly, currentUser has to be populated through the credentials taken from the userInput and passInput states just before the next function, which is useEffect, feeling so stuck with that and any help will be very appreciated

link to sandbox

https://github.com/Abdulberk/frontend-auth
type User = {
  username: String | null;
  password: String | null;
}

function Login() {
  const [userInput, setUserInput] = useState<String | null>("");
  const [passInput, setPassInput] = useState<String | null>("");
  const [isLoggedin, setIsLoggedin] = useState<Boolean>(false);
  const [isLoading, setIsLoading] = useState<Boolean>(false)
  const [error, setError] = useState<Boolean>(false);
  const [count, setCount] = useState(0);
  const [auth, setAuth] = useState<String | null>("");
  const navigate = useNavigate();

  const [currentUser, setCurrentUser] = useState<User>({
    username: "",
    password: ""
  });

  const submitHandler = (event: React.MouseEvent<HTMLButtonElement>): void => {
    event.preventDefault();
    setIsLoading(true);
    setCurrentUser(prev => {
      return {
        ...prev,
        username: userInput,
        password: passInput
      }
    });

    setCount(prev => prev + 1)
  }

  useEffect(() => {
    axios.post("https://reqres.in/api/login", {
      email: currentUser.username,
      password: currentUser.password
    })
      .then((response: AxiosResponse<any, any>) => {
        console.log(response)
        if (response.status === 200) {
          console.log("user exists");
          setIsLoading(false);
          setIsLoggedin(true)
          setAuth(response.data.token)
          setCount(prev => prev + 1)
        } else {
          console.log("user does not exist");
          setIsLoading(false);
          setIsLoggedin(false);
          setAuth(null)
          setCount(prev => prev + 1)
        }
      })
      .catch((error: any) => {
        setAuth(prev => prev)
        setIsLoggedin(false)
        setIsLoading(false);
        setCount(prev => prev + 1)
      })
  }, [currentUser.username, currentUser.password])

  if (isLoggedin) {
    setTimeout(() => {
      console.log("redirecting to profile page");
      navigate(`users/${currentUser.username}`, { state: currentUser });
    }, 2000);
  }

  useEffect(() => {
    console.log(`${count} renders`);
  }, [count])

  return (
    <div>
      <Form>
        <LoginContainer>
          <LoginImageContainer>
            <LoginImage src={require('../right.png')}/>
          </LoginImageContainer>

          <LoginTitle>Login your account</LoginTitle>
      
          <InputContainer >
            <FaUserCircle 
              fontSize="30px"
              color="#747474"
              style={{ position: "absolute", top: "50%", transform: "translateY(-50%)", left: "20px" }}
            /> 
            
            <LoginInput
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => setUserInput(e.target.value)} 
              placeholder="Username"
            />
          </InputContainer>
    
          <InputContainer >
            <RiKeyFill style={{ left: "20px", top: "50%", transform: "translateY(-75%)", color: "#747474", fontSize: "32px", position: "absolute" }} />
            
            <LoginInput
              placeholder="Password" 
              style={{ marginBottom: "20px" }}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPassInput(e.target.value)} 
            />
          </InputContainer>
        
          <Button 
            onClick={submitHandler} 
            className={isLoading? 'lds-dual-ring': undefined}
            type='submit'
            style={{ backgroundColor: "#7FBCD2", marginBottom: "20px" }}
            size="lg"
            variant="primary"
          >
            {!isLoading ? "Login" : undefined}
          </Button>
        </LoginContainer>
      </Form>
    </div>
  )
}

export default Login;

Advertisement

Answer

Alright I gave it a try even though your sandbox was empty. Check it out here. It’s a stripped out version of what you shared here.

The gist of it is that your login attempt pattern is odd. The submit handler should be submitting the login with the current state and the input onChange should be updating the currentUser directly.

On top of that your redirect should be handled in a useEffect. Let me know if you have questions or I missed something!

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement