understanding async behavior of use Effect hook working - reactjs

const [count,setCount]=useState(5)
useEffect(()=>{
setCount(10)},[count])
In above code if i am updating state without async operation useEffect doesn't goes to infite loop.While in below code if i update sate through asysnc operation it goes to infite loop can some one tell me the behavior of useEffect in the case of async oeration?
const [count,setCount]=useState(5)
useEffect(()=>{
//some async operation
.then(response)=>{
setCount(response.data)}},[count])

This is because numbers are compared to values and objects by references. Each time async operation result in new object reference, hence useEffect triggers.
In case of number like setCount(5) will not trigger again as second time 5 is the same variable as previous.

Thats a good question!
In the first snippet you were setting the count to 10, that value is not changing dynamically and hence it will change once and wait for the count to change again which will not happen over there, whereas in the second snippet once the async operation is completed it will update the count and because of this count update useEffect will be triggered again along with the async operation in it. So it goes into a recursive updation.
If you want, you can change the first snippet like this to see the recursive updation
setCount(count + 1);

This is the catch of hooks and how they update state, if your previous state value and current state value is same then react functional component won't re-renders, so it is advisable if you are working with object type of state then always create new object or use spread operator for updating any property inside object while updating your state. In your case setCount(10) will always return a constant value of 10 so useEffect won't trigger infinite times as count is theoretically changed once from 5 to 10, but for an API request it will always returns an object with new reference so it'll create an infinite loop. You can also do setCount(10) inside your API result and it also won't trigger useEffect as count is basically never changed from 10 except from a change 5 to 10.

Related

React state change is one step behind

A common problem in React, especially among beginners (like me). This is due to the fact that setState is an asynchronous function and React does not change values one by one, but accumulates some pool if it is not otherwise instructed. (please correct me if the wording is wrong)
So here's the question. One simple solution is to put the desired state into the useEffect dependency array.
For example, I have such a component:
const [first, setFirst] = useState()
const [second, setSecond] = useState()
useEffect(() => {
Something logic by first and second...
}, [first])
Here, the useEffect is executed every time the first state changes. At the same time, at the moment of its operation, the second state is not relevant, next time it will become as it should be now, and so on. Always one step behind.
But if I add a second state to the useEffect dependency array, then everything works as it should.
useEffect(() => {
Something logic by first and second...
}, [first, second])
At the same time, it is not necessary that the useEffect work on changing the second state, I added it only in order to have an up-to-date version inside the useEffect. Can I use the useEffect dependency array this way?
if you use useEffect with an empty array it will act as componentDidMount and componentWillUnmount so it will only invoke itself on first component creation and when it is about to unmount.
https://reactjs.org/docs/react-component.html#componentdidmount
Here I got the problem You are facing, I am not fully sure, but In my case I was console.log(result) withing the fucntion that was changing state, but it was always one step behind. why was that? because in React it is considered as side effect. So If you console.log(result) in useEffect passing the value in dependency array then, it will console log the same value that instantly changed.
In backgorund the state is updating exactly the same time but useEffect detects it as exactly as it is changed.
You can write any logic in useEffect as well or in the function which you are updating.
So there should not be any problem writing logic in the function which you are callilng on click.

Why would a value get stale using closure in React?

In the documentation for useEffect() it shows the following example:
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
}
It then says:
Experienced JavaScript developers might notice that the function
passed to useEffect is going to be different on every render. This is
intentional. In fact, this is what lets us read the count value from
inside the effect without worrying about it getting stale. Every time
we re-render, we schedule a different effect, replacing the previous
one.
I don't understand why count would get stale inside useEffect() since useEffect has count within its closure. Since useEffect() is a hook, shouldn't it always have access to the latest state variable? Also, in general, how can I be sure that I always have the latest value for a state variable?
I don't understand why count would get stale inside useEffect() since useEffect has count within its closure.
Keep in mind that Example is going to be called multiple times, once for every render. So there are N renders, N count variables, and N useEffect functions. So it's true that each effect function has count within its closure, but each of them has a specific count within its closure. Whatever value it had when the closure was created, that's the value it will have when the effect runs. If you only created the function once, closing over the first value of count, then that code would only ever see the first value of count (ie, 0).
Also, in general, how can I be sure that I always have the latest value for a state variable?
If you're setting state, then you can always access the latest value by using the function version of setState. For example:
setCount(prev => /* calculate new state from prev */)`
For other cases, either include the state in the dependency array of your useEffect, so that the effect re-runs when the count changes:
useEffect(() => {
// some code that uses `count`
}, [count]);
Or leave the dependency array off entirely if you want the effect to run on every render.
useEffect(() => {
// some code that uses `count`
});

Is this only a syntax difference `useState()`

const [val,setVal] = React.useState(0);
Sometimes I see this in other peoples code
setVal(()=>val);
Does that have any difference from this.
setVal(val);
As the comments point out, half the time this syntax makes no difference. Where it does comes into play is asynchronisity. For example, say you have a setTimeout call in a useEffect and it waits a few seconds and then performs a state update, but while awaiting, the state is updated from somewhere else in the component. If you were to reference the state value in the setTimeout callback it would be a stale value and so if you called setMyState(prevState + 1) you would be off by 1. However if you use setMyState(prevState => prevState + 1) you have accessed the most up-to-date state value.
Also, it is very useful when you don't want to reference the state value in a hook like useEffect. Anything included in a dependency array will cause the hook to update and that can produce chain reactions or fire off a useEffect. when using setMyState(prev => prev + 1) inside the hook, you can avoid referencing the state value itself.

React js state value late update

set the state value in Useeffect function and want to use that value after 5 lines in filter function but the state show the empty value means it update the state late how to solve this error
setState is an asynchronous function. Let's take this example
const [testState, setTestState] = useState()
and in a useEffect, if you call lets say setTestState('1234') and the very next line you try to log it console.log(testState) - this wont log '123' as the setTestState is asynchronous, which is happening in your case.
Solution -
Put the filter function in a different useEffect and pass the state in the dependency array

useState with `new` keyword as parameter

I'm was building a separated service that deals with a complex things, the service is a class that, just for testing proposes, I mock inside a useState.
The point is that I forgot a console.log inside the constructor and realize that the class constructor is called so many times as the component is re-rendered. That behavior don't lead to unexpected behavior or something like that, but I'm asking myself WHY this is happening, as I know things declared inside a useState on it's call don't repeat itself, but apparently I'm wrong what leads to the questions below.
Why this happens? (I don't find any docs about this specific case)
Does this affect memory or processing? (Since the class is re-instantiated many times)
The garbage collector collect that?
I create a little sandbox to example what I'm saying, you can see that the "Called" word is displayed many times on console, and keeps displaying clicking on the button.
https://codesandbox.io/s/new-class-inside-usestate-w9et3?file=/src/App.js
It is a common mistake and somewhat not explicitly mentioned in React docs.
On each render the body of function component executed.
Logging from the constructor is the expected behaviour, because writing such code:
const [example] = useState(new Foo());
Will results calling new Foo() on every render, yes, although it's result not considered by useState hook.
Therefore you would like to make lazy initial as you want it to be called once:
const [example] = useState(() => new Foo());
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render.
Cus useState(/* whatever expression here */) is still and JS expression. Nothing to do with React, it's just how JS work, the thing inside parens () will always evaluate.
Let's leave useState aside, think about some randomFunction(), if you do:
setInterval(() => {
randomFunction(new RandomClass());
}, 1000)
Will RandomClass be instantiated every 1 sec? Of course it will.
Same thing happens in React.
function MyApp() {
const [myClass, setMyClass] = useState(new RandomClass())
// ...
}
Everytime <MyApp /> got re-rendered, the function must re-run, so must RandomClass() be re-newed. What mislead you is the effect of useState(). It takes the expression passed inside parens as it's initial value at first render, and will discard whatever got passed in in following re-render. But that expression still evalutate.
It is simply an expression and it evaluates to an instance of that class. It just so happens that the constructor also logs some data. The same behaviour can be replicated by using an iife i.e., useState((function(){console.log("Called")})());. That does not necessarily mean it will set a new instance of that class for your state because it has already been set the 1st time your component rendered.
This theory can be tested in a useEffect
React.useEffect(()=>{
console.log(exemple === exemple)
})
you will see that it returns true because it is a reference to the same class and no state change has occured. The impact on your app in this setting is the time complexity increases because it does instantiate a new class every render occurs.

Resources