handleClick=()=>{
setOne(1)
setTwo(prevState=>{ return prevState+1})
setTimeOut(() =>{ setThree(3) },0)
}
If handleClick is called, why component renders 2 times, first 2 state updates are done at once and another one inside settimeout is done later, why ?? Why it can't render in one shot.
The behaviour is due to Reactjs's batching update
This means that all setState inside an eventHandler/function is batching to update synchronously.
The setThree(3) updates later and not being batched is because the fact that it's inside another function scope (setTimeout's callback).
Related
In the following code from reactjs.org:
useEffect(() => {
function tick() {
// Read latest props at any time
console.log(latestProps.current);
}
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // This effect never re-runs
as my brain compiles it, the effect created is a "wait one second then do something", but the setInterval being async itself, it returns immediately then useEffect return its closure callback. React being aware of states changes only and not of actions (it doesn't know what was launched in the useEffect, isn't it? How could he know!), I suppose that it'd fire the closure callback directly on return and then prevent the tick() function to be fired even ounce... but it's not the case. How comes ? How React knows what to wait before firing the closure callback returned by useEffect?
While I don't know exactly what happens in the background, for implementation sake you need only to know that the return callback of a useEffect is only called when the effect is re-ran, or more specifically, after "closing" the previous effect-run and before the new effect running. Depending on the effect's dependencies, it can be on every render, or (as in the example you posted) only when the component is unmounted.
It's useful to think that functional components are just functions, so unless the function (the component) is called again (a re-render or other lifecycle change), the effect is "stopped", there's no magical parallel process. I would risk saying that react checks the hooked effects on a component in pre and post render. Depending on dependencies and the effect's details, it knows whether it should call the return callback or not call the effect at all.
See this sandbox I created where I demo these two most extreme cases: effect on every render, and effect only on mount/unmount. Check the sandbox console to understand the behavior. Try to change the parent's effect dependencies to [count] and see the differences.
PS: when I started using hooks, this article helped me a lot https://overreacted.io/a-complete-guide-to-useeffect/
I am beginner in react and I am learning about lifecycle hooks. I came across some articles stating don't call setState synchronously inside componentDidMount. But we can setState in then block of promise.
The reason mentioned was setState will cause re-render of the component, which will affect performance.
My question is, even if I write setState inside then block of promise, re-render will be there anyways, also setState is asynchronous so it won't be called immediately. So why its not recommended to call setState inside componentDidMount synchronously, And how does writing inside then block of promise solves the problem if there are any.
PS: I have researching on SO for quite a while, but couldn't find answers that says don't call setState synchronously, I found some related answers, but didn't answer my question, and I couldn't get them because they were totally related to some scenario.
React setState calls are asynchronous. React decides when to do it efficiently. For example, multiple setState calls are batched together into one single update. The way to get around this is using setStates's 2nd parameter which is a callback that will execute once setState is completed and the component re-rendered.
handleThis = () => {
this.setState({someVar: someVal}, funcToCallimmediately )
}
To test, console.log the state variable just after setState() and write another console.log inside the "funcToCallimmediately" function.
đź“ť Take note that if you need to set the state based on the previous state you should use another method. First, just know that setState's 1st parameter can be an object and can also be a function. So you do it like this...
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
To understand more, read more about setState() here. You'll get what you need.
componentWillMount() {
let dID=this.props.match.params.datumID;
this.setState({datumID: dID});
console.log(dID);
console.log(this.state.datumID);
}
I'm just trying use setState() method.
But the problem is it is not working.
Here is what I get as output:
First console.log() : 66
Second console.log(): null
this.setState accepts a second argument as callback which is fired after the state is successfully changed.
componentWillMount() {
let dID=this.props.match.params.datumID;
console.log(dID);
this.setState({datumID: dID},()=>console.log(this.state.datumID));
}
side note : Move your code to componentDidMount.
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method
will not trigger a re-render. Avoid introducing any side-effects or
subscriptions in this method.
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
If you need to set the state based on the previous state,
read about the updater argument below.
this.setState((prevState, props) => {
return {datumID: props.match.params.datumID};
});
Because SetState() is an asynchronous function, use below code
componentWillMount() {
let dID=this.props.match.params.datumID;
this.setState({datumID: dID},
function(){
console.log(this.state.datumID)});
console.log(dID);
}
and if possible try to avoid compenentWillMount since it would be deprecated soon
setState() being an asynchronous function it is in progress of updating the state and javascript being single-threaded language it will execute the next line of code. So if you want to see the state value set you have to do something like this.
componentDidMount=()=> {
let dID=this.props.match.params.datumID;
this.setState({
datumID: dID
},()=>{
console.log(dID);
console.log(this.state.datumID);
});
}
Also I would recommend you to use componentDidMount as willMount won't work in the future as it is deprecated
this.setState is an asynchronous function that takes time and passes the newly assigned
state value over all of the React's life-cycle functions in order to update a state and trigger the re rendering of a component. Your first console logs out the value that is assigned to set state and the next console logs out the value that is currently residing in the state this.state.datumID. if you need to perform operations after setting state you can pass a callback that is triggered after a state is successfully updated.
this.setState({datumID: dID}, () => console.log(this.state.datumID) ); // this will log the updated value
Another thing I want to point out is componentWillMount will wont work in the future release. If you want to do something after DOM is rendered, use componentDidMount or you can perform pre-render task in the constructor of your Class Component.
I see that setState is async. So if I were to call:
this.setState({ variable: true });
and immediately call:
this.setState({ variable: false });
before render is called, am I guaranteed that 'variable' will be false when React is finished processing? In other words, are the async operations sync? Will render be called twice, or will 'variable' be overwritten and render called once with variable=false?
From the react docs for setState:
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
So your logic should not rely on setState execution time. If you like to control render you should consider using shouldComponentUpdate(nextProps, nextState).
Use shouldComponentUpdate() to let React know if a component’s
output is not affected by the current change in state or props. The
default behavior is to re-render on every state change, and in the
vast majority of cases you should rely on the default behavior.
It is best not to rely upon this behavior. It will only work sometimes and not others.
To reliably set multiple state properties, gather all the updates and set them in a single call:
const changes = {};
if (some logic) { changes.variable = true; }
if (some more logic) { changes.variable = false; }
this.setState(changes);
Great question! Made me curious as well, so I whipped up this JSFiddle demonstrating this behavior.
React calls render synchronously after your event handler.
Meaning that because your are doing two updates in the same function, it won't re-render until after the function completes. That means that in each render, this.state.variable will be false.
Usually timers will be cleared before unmounting the component from DOM. But what would be the side effects if we forgot to clear the timers?
Suppose you call a timer in some function and when you navigate to another component and your current component has unmounted, if you do not clear the timer, your function will continue to be executed.
Hence in the componentWillUnmount function you need to clear the timer which can be identified by the numeric value returned by setInterval
AS mentioned in the React DOCS:
componentWillUnmount() is invoked immediately before a component is
unmounted and destroyed. Perform any necessary cleanup in this method,
such as invalidating timers, canceling network requests, or cleaning
up any DOM elements that were created in componentDidMount
Example:
componentDidMount() {
this.timerID = setInterval(
() => this.somefunc(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
SideEffect:
Consider a case when in the timer you are making the API call from which you are getting data that you display in your component. Now if you navigate away from the component you wouldn't normally want to be calling the API again and again even though you don't need the result. This will cause and Unnecessary load on the Server.