skip to Main Content

I capture user/pass info and store it in a useState called login inside Login.tsx and pass it up to App.tsx and then store the access property from login useState to access useState inside the App.tsx

This does work, however, it seems it is one state behind, and have to click the login button twice to go to the next page, which is supposed to be the HomePage.

(App.tsx)

import { useState } from "react";
import "./App.css";
import HomePage from "./pages/home/Home";
import LoginPage from "./pages/login/LoginPage";

function App() {
  const [access, setAccess] = useState(false);

  const onLoginInfo = (onLoginInfo: any) => {
    setAccess(onLoginInfo.access);
    console.log("***<APP>***");
    console.log(onLoginInfo);
    console.log("***</app>***");
  };

  const Greetings = () => {
    if (access) {
      return <HomePage />;
    } else {
      return <LoginPage onLoginInfo={onLoginInfo} />;
    }
  };

  return (
    <div className="App">
      <h1>Welcome to Oylympus!</h1>
      <Greetings />
    </div>
  );
}

export default App;

(LoginPage.tsx)

import React, { useState } from "react";

import classes from "./Login.module.css";

const LoginPage = (props: any) => {
  const [login, setLogin] = useState({
    user: "",
    pass: "",
    access: false,
  });

  const user = "abc";
  const pass = "123";

  const loginSubmitHandler = (event: any) => {
    event.preventDefault();

    if (
      event.currentTarget.username.value === "abc" &&
      event.currentTarget.password.value === "123"
    ) {
      setLogin({
        user: event.currentTarget.username.value,
        pass: event.currentTarget.password.value,
        access: true,
      });
    } else {
      setLogin({
        user: event.currentTarget.username.value,
        pass: event.currentTarget.password.value,
        access: false,
      });
    }

    props.onLoginInfo(login);
    console.log("submit successfull");
  };

  const checkInfoHandler = () => {
    console.log("user: " + login.user);
    console.log("pass: " + login.pass);
    console.log("access: " + login.access);
  };

  return (
    <div className={classes.login}>
      <p>Login Page</p>
      <form onSubmit={loginSubmitHandler}>
        <div>
          <label>
            User:
            <input type="text" name="username" />
          </label>
        </div>
        <div>
          <label>
            Password:
            <input type="password" name="password" />
          </label>
        </div>
        <button className={classes.button}>Login</button>
      </form>
      <div>
        <button
          className={classes.button}
          type="button"
          onClick={checkInfoHandler}
        >
          Check info
        </button>
      </div>
    </div>
  );
};

export default LoginPage;

I tried writing it a different way but still, the same thing is happening, then I tried to use a useEffect to reload react component but didn’t like it.

2

Answers


  1. Calling setLogin(...) in the LoginPage triggers a re-render of that component, but the value of the login variable doesn’t actually change—that only changes when the function is re-run as part of the re-render. You can see this in the State as a Snapshot documentation topic, specifically the State over time sample, which is similar to your code:

    import { useState } from 'react';
    
    export default function Counter() {
      const [number, setNumber] = useState(0);
    
      return (
        <>
          <h1>{number}</h1>
          <button onClick={() => {
            setNumber(number + 5);
            alert(number);
          }}>+5</button>
        </>
      )
    }
    

    Here, like your code, the setNumber(...) is called to update the state, but alert(...) is called with the value of the current state, which is still 0. The state will not be updated until after the callback finishes, and even then it will do so by re-rendering the component (the number variable will never change within a single function execution; if a re-render is required, the function will re-run and useState(0) will return the new value, 5, to the new number variable).

    Similarly, when you call props.onLoginInfo(login), it is still passing in the "old" value of the login state, because that is the value of that variable within the current render.


    There are a couple of ways I can see to make this work. First is to ask whether LoginPage even needs to keep state. Nothing in that component actually uses it (aside from checkInfoHandler, which looks like it might just be for debugging, but even that doesn’t necessarily need to use state), and the App component is the one responsible for maintaining that state for the purposes of determining whether to render the LoginPage or the HomePage component. In that case you could eliminate all of the state management from LoginPage and just call the props.onLoginInfo(...) callback with the new value:

    const loginSubmitHandler = (event: any) => {
      event.preventDefault();
    
      if (
        event.currentTarget.username.value === "abc" &&
        event.currentTarget.password.value === "123"
      ) {
        props.onLoginInfo({
          user: event.currentTarget.username.value,
          pass: event.currentTarget.password.value,
          access: true,
        });
      } else {
        props.onLoginInfo({
          user: event.currentTarget.username.value,
          pass: event.currentTarget.password.value,
          access: false,
        });
      }
    
      console.log("submit successfull");
    };
    

    If LoginPage does need state for something, then you could keep the setLogin(...) state calls but just make sure to call props.onLoginInfo(...) with the same value. There are other ways, too, but they are a little more advanced.

    const loginSubmitHandler = (event: any) => {
      event.preventDefault();
    
      const isLoginSuccessful =
        event.currentTarget.username.value === "abc" &&
        event.currentTarget.password.value === "123";
    
      const newLogin = {
        user: event.currentTarget.username.value,
        pass: event.currentTarget.password.value,
        access: isLoginSuccessful,
      };
    
      setLogin(newLogin);
      props.onLoginInfo(newLogin);
      console.log("submit successfull");
    };
    
    Login or Signup to reply.
  2. You can try this:

    import * as React from 'react';
    import { useState } from 'react';
    
    const USER_NAME = 'abc';
    const USER_PASSWORD = '123';
    
    const LoginPage = ({ onLoginInfo }) => {
      const [login, setLogin] = useState({
        user: '',
        pass: '',
        access: false,
      });
    
      const loginSubmitHandler = (event: any) => {
        event.preventDefault();
        const user = event.currentTarget.username.value;
        const pass = event.currentTarget.password.value;
        const access = user === USER_NAME && pass === USER_PASSWORD ? true : false;
        const _login = { user, pass, access };
        setLogin(_login);
        onLoginInfo(_login); //go home page
      };
    
      const checkInfoHandler = () => {
        console.log('submit successfull');
    
        console.log('user: ' + login.user);
        console.log('pass: ' + login.pass);
        console.log('access: ' + login.access);
      };
    
      return (
        <div>
          <p>Login Page</p>
          <form onSubmit={loginSubmitHandler}>
            <div>
              <label>
                User:
                <input type="text" name="username" />
              </label>
            </div>
            <div>
              <label>
                Password:
                <input type="password" name="password" />
              </label>
            </div>
            <button>Login</button>
          </form>
          <div>
            <button type="button" onClick={checkInfoHandler}>
              Check info
            </button>
          </div>
        </div>
      );
    };
    
    export default LoginPage;
    
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search