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
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:Here, like your code, the
setNumber(...)
is called to update the state, butalert(...)
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 (thenumber
variable will never change within a single function execution; if a re-render is required, the function will re-run anduseState(0)
will return the new value, 5, to the newnumber
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 theprops.onLoginInfo(...)
callback with the new value:If LoginPage does need state for something, then you could keep the
setLogin(...)
state calls but just make sure to callprops.onLoginInfo(...)
with the same value. There are other ways, too, but they are a little more advanced.You can try this: