React Hooks state always one step behind [duplicate] - reactjs

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 years ago.
I have various functions in React where I need to change the state with useState then do some action conditional on whether the new state meets some criteria.
This uses a setValues method in useState to set the value of newpassword when handleChange is called with prop="newpassword". The new password is then evaluated with a regex test, and if it is valid the state variable passwordIsValid should be set to true.
const handleChange = prop => event => {
setValues({ ...values, [prop]: event.target.value })
if (prop === 'newpassword' && passwordValidation.test(values.newpassword)) {
setValues({ ...values, passwordisValid: true })
console.log(prop, values.passwordisValid)
} else {
console.log(prop, values.passwordisValid)
}
}
The state is always one step behind tho - and I know this is because useState is async, but I don't know how to use useEffect to check the state? Very new to hooks, could someone help me out?

useState() hook is just a function call. It returns value and function pair. values is just a constant it doesn't have any property binding.
// Think of this line
const [values, setValues] = useState(0);
// As these two lines
const values = 0;
const setValues = someFunction;
When you call setValues react updates value for the next render. Next time component renders, it will call useState again which will return new values.
As a solution you should use event.target.value. You don't want to use it directly though because event.target is nullified after you observe it.
const newValue = event.target.value
// use newValue to setValues etc

Inside any particular render, props and state forever stay the same and Every function inside the component render (including event handlers, effects, timeouts or API calls inside them) captures the props and state of the render call that defined it. For that reason if you try to access values.newPassword in your event handler you will always get the state for that particular render i.e the old password.
Just think of useState as a function that returns the state for that particular render and that state is immutable for that particular render.

Related

Why do we need to use setValue in UseState?

So, React has UseState hook, and we should use it like this.
const [value, setValue] = useState(0);
And if we want to change a value, it should be written like this:
const increase = () => {
setValue(value + 1)
}
Why it's not possible to write it like this? Why we need setValue?
const increase = () => {
return value + 1;
}
I understand that it just doesn't work, but I couldn't find an explanation why it is like that.
The useState hook returns a function to update the state. Using setValue(value) method, you can get the previous(or old) state value to update the state.
On the other hand, without useState when you tries to increment the value, the value will not change because the react rendered the component only once and since there is no state change it won't get re-rendered, the value will remain at 0.
You can see here in details : https://dev.to/aasthapandey/why-to-use-usestate-in-react-pkf
Answer is simple...! The state variable is directly linked to rendering your page. The state variable of useState hook is read only for the component to render it in the below return statement which renders the JSX.
The setValue() function not only changes the state value, but will also trigger the react to re-render the DOM which has already being painted on the browser.
Well that's a difference between an ordinary HTML page & a React App. React provides amazing framework to re-render the page automatically after the you alter the state using the setValue() fucntion

Can I await the update function of the useState hook

I am building an app to understand the useState hook. This app simply has a form for entering username. I am trying to save the entered username. So, I have used react useState. And I tried to await the updating function of the useState in the event handler.
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
And when I tried to log the username it doesn't show us the current state but the previous state. Why?
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
enteredUsername is never going to change. It's a closure variable that's local to this single time you rendered the component. It's usually a const, but even if it was made with let, setEnteredUsername does not even attempt to change its value. What setEnteredUsername does is ask react to rerender the component. When the render eventually happens, a new local variable will be created with the new value, but code from your old render has no access to that.
If you need to run some code after calling setEnteredUsername, but you don't actually care if the component has rerendered yet, the just use the value in event.target.value, since you know that's going to be the new value of the state:
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
console.log(event.target.value, enteredAge);
}
If instead you need to make make sure that the component has rerendered and then do something after that, you can put your code in a useEffect. Effects run after rendering, and you can use the dependency array to make it only run if the values you care about have changed:
const [enteredUsername, setEnteredUsername] = useState('');
useEffect(() => {
console.log('rendering complete, with new username', enteredUsername);
}, [enteredUsername]);
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
};
the act of setting state is asynchronous; therefore, console logging directly after setting your state will not accurately provide you with how state currently looks. Instead as many have suggested you can utilize the useEffect lifecycle hook to listen for changes in your enteredUserName state like so:
useEffect(() => {
console.log(enteredUsername);
}, [enteredUsername]);
listening for changes within the useEffect will allow you to create side effects once state has updated and caused your component to rerender. This in turn will trigger your useEffect with the enteredUsername dependency, as the enteredUserName state has changed.

React - useState returns initialized value even after manipulating it

This is my code:
import React,{useEffect, useState} from 'react';
import bootstrap from '../../node_modules/Bootstrap/dist/css/bootstrap.min.css'
import '../ComponentStyles/NavbarStyle.css'
function Navbar() {
const [LUsername,setLUsername]=useState('')
const [LPassword,setLPassword]=useState('')
const [IsLogin,setIsLogin]=useState(false)
function Login(){
let credentials = JSON.parse(localStorage.getItem('cred'))
console.log(credentials)
for(let i=0;i<credentials.length;i++) {
if(credentials[i].username===LUsername && credentials[i].password===LPassword){
alert('in')
// why does IsLogin value is false even after setting it to true
setIsLogin(false)
alert(IsLogin)
}
}
if(IsLogin){
alert('login')
}
else{
alert('Login Failed')
}
On line 7 I have initialized Islogin variable with useState.
On line 18 I am using the setIsLogin method to change it's initial value from false to true
but when I use alert to output it's value it gives me the initial value which is false.
Also the value returned is true when I use conditional rendering in line 45, It works correct over there,
Can someone tell me why the value is shown wrong in alert?
Thanks.
Returns a stateful value, and a function to update it. The function to update the state can be called with a new value or with an updater function argument.
As you can see your code, state right after calling setState will have value before the update.
We have some options.
Calling updater just to get the latest value.
const [value, setValue] = useState(false);
setValue(true);
setValue((state) => {
console.log(state); // true
return state;
});
Custom hook for setState with async state selector.
const [value, setValue, getValue] = useSetState(false);
setValue(true);
console.log(await getValue()); // true
You can try to use useEffect to output IsLogins value when the value changes.
useEffect(() => {
alert(IsLogin)
},[IsLogin]);
insert this snippet right under "const [IsLogin, setIsLogin] = useState(false)"
According to React's documentation:
State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance.
You can not relay on setState to mutate the state synchronously.
In order to do something (effect), you should use useEffect on that state variable.

In React, what's the difference between a setState callback and using useEffect on a state variable?

I'm confused about whether I should be using a setState callback or the useEffects hook when updating state in my application. On my dashboard, I have a loggedInUser object, which has these fields
cashAssets
stockASsets
bondAssets
From my dashboard, I have components with execute transaction buttons, which work similarly,
<TransactionButton type={type} onClick={handleOnClick} />
When someone executes a transaction, that could affect all of the three above fields for a loggedInUser. From my dashboard component, I'm uncertain about whether I should set these 3 fields of my user object as three state variables and further how should they be updated -- using a setState callback or setting them up via useEffects, e.g.
useEffect(() => {
// Add logic to update all three
}, [cashAssets, stockAssets, bondAssets]);
or should I be treating the loggedInUser object as a state variable?
You do not need to implement useEffect to update state values in response to an event. You can update the state in event handler itself.
Since you want to update all three fields of the state object you can just call setState update function with the new object with updated fields or if the fields depend on previous values then use functional setState
const handleClick = () => {
// name your setState whatever you call it from useState
setState(prev => ({
...prev,
cashAssets: // new value here,
stockASsets: // new value here
bondAssets: // new value here
})
}

What is the right way to update the state instantly?

Sorry if someone has already answered this question, but I didn't find what I am looking for.
I recently started learning react and notice that there are quite a few ways to set the state. For example, I have a counter in an object, and I want to increase it.
const [state, setState] = React.useState({ counter: 0 });
And all the functions below give the same result, but as I understood, they do it asynchronously.
setState({ ...state, counter: counter + 1 }):
setState(() => ({ ...state, counter: counter + 1 }));
setState(prevState => ({...prevState, counter: counter + 1 }));
setState(counter = counter + 1);
How can I update the state instantly and properly after calling the setState function? Thank you in advance!
In order to do some logic with the newly updated value of the state, you should use
The UseEffect Hook
useEffect(() => {
/* use the new state value here */
}, [state])
or, [if you're using component classes], the callback function
this.setState({ ...state, counter: counter++ },
() => { /* use the new state value here */ }
);
Also for a simple counter and in order not to be confused betweeen react components and react hooks... I would recommend using the useState like this :
const [counter, setCounter] = React.useState(0);
setCounter(counter++);
What do you mean by
How can I update the state instantly and properly after calling the setState function?
?
You actually are updating state in the setState function, so the question itself sound strangely.
If you want to perform some action synchronously right after new value will be set, you can use callback function, which was mentioned by Seba99. Here are link to docs (basically it's setState docs). Optional callback is executed after state update, and this.state inside it will always be up-to-date with latest changes, you've made in setState.
So, if you need to synchronously get latest state and perform some actions with it (even update state one more time) - use callback of this.setState.
How can I update the state instantly
You can't, because you've already declared your state with const, right?
const /*const*/ [state, setState] = React.useState({ counter: 0 });
function handleClick() {
setState(anything)
// you declared state with const, you "obviously" shouldn't expect this
// to "somehow" immediately change `state` to anything
}
You can only make it work as expected even when state's updated asynchronously, or not instantly, depends on circumstances. If you want to get the newly updated value of your state to use later in a consequence, cache that new value to use is the right way. For example:
const newStateA = changeState(stateA)
setStateA(newStateA)
setStateB(calculateBFromA(newStateA))
// instead of
setStateB(calculateBFromA(stateA)) // will get the old value of stateA
Of course, you could just set state = something if you haven't declared it with const, like let or var instead, it would change state instantly, but it wouldn't tell React to rerender the component later with the newly updated state value, unless you just set it with with the "second array-destructured" param setState (talking about Hooks), and btw this (declare state with let/var`) is obviously the wrong way

Resources