why componentDidUpdate run multiple times - reactjs

I really don't understand the react life cycle. I run console.log in componentDidUpdate() and saw that it ran multiple times
Console showed that componentDidUpdate ran 3 times

Maybe I can give you an extra example when your issue happens since I cannot see your code.
componentDidUpdate(prevProps, prevState) {
const { something } = this.props;
if (prevProps.something !== something) this.apiCall();
console.log('something')
}
when you change your state or props, componentDidUpdate is being invoked, and apiCall function makes http request via fetch or axios, and change the state twice using setState function.
whenever state gets changed, new render() is being invoked and componentDidUpdate follows.
but the condition
if (prevProps.something !== something) this.apiCall();
may not be satisfied anymore, just console logging at this time instead of calling apiCall function which would trigger inifinite loop.
hope it helps.

componentDidUpdate() is invoked immediately after updating occurs.
your problem may be accorded because the state has been changed, props received or parent re-rendred.
if you are new with React, I recommend you to read the following article:
Post-Render with componentDidUpdate()

Related

How to prevent infinite loop in componentDidUpdate

I've got this code but it gives me an infinite loop.
How can I prevent it?
Or I need to use another lifecycle method??
componentDidUpdate(prevProps, prevState) {
if(prevState.input["purpose"] === "TEST" ){
this.setState({input: { ...this.state.input, number: "1"}})
}else if(prevState.input["purpose"] !== "TEST"){
this.setState({input: { ...this.state.input, number: "0"}});
}
console.log(this.state.input.number);
console.log(prevState.input["purpose"]);
}
In both cases of if-else, you are updating the state and you are checking for the purpose to be 'Test'
if its 'Test' you are updating the state and if it's not still you are updating state
You are updating the state in componentDidUpdate. Therefore, this function is called every time.
Both of your conditions call setState.
Following docs from React.js. You should not update state like this.setState inside componentDidUpdate without condition
https://reactjs.org/docs/react-component.html#componentdidupdate
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop. It would also cause an extra re-rendering which, while not visible to the user, can affect the component performance. If you’re trying to “mirror” some state to a prop coming from above, consider using the prop directly instead. Read more about why copying props into state causes bugs.
In this case, your if else condition always update state
Please, follow react component lifecycle as below

React setState is not getting updated

I have added the below piece of code in my react project to set the state with the response of a method. But the state never updated. It is always null. May i know how to set the state here
_onload():any{
this._DataAccessObj = new DataAccess();
let result = this._DataAccessObj.getRequest(this.props.itemId).then((item:IRequest) =>{
console.log("item");
console.log(item);
this.setState ({
Request: item
});
console.log(this.state.Request);
console.log("setstate");
});
return;
}
From the documentation of ReactJS and lifecycle:
Every second the browser calls the tick() method. Inside it, the Clock
component schedules a UI update by calling setState() with an object
containing the current time. Thanks to the setState() call, React
knows the state has changed, and calls the render() method again to
learn what should be on the screen. This time, this.state.date in the
render() method will be different, and so the render output will
include the updated time. React updates the DOM accordingly.
The last part is important: The actual state object will be update later on the line, not directly when the setState method has been called.
On the same page:
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.

this.setState() not working properly - react.js

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.

Render is called twice when fetching data from a REST API

I am trying to interact with a REST API using React, and I have realized that when I fetch the data, render is called once without the data, and then again with the data.
This throws an exception when I try to process this data, but I can use an if statement to check if data is null or not. However, I am not sure if that's needed.
class App extends Component {
state = {
TodoList: {},
};
componentWillMount() {
axios.get("http://localhost:5001/1").then((response) => {
this.setState({
TodoList: response.data,
});
});
}
render() {
console.log(this.state);
return <h1>hello </h1>;
}
}
This is what I see in in the console:
That's perfectly normal.
Your App component flow as below:
Execute render method to load the component
execute codes in componentDidMount
Calling axios.get which is async operation
Receive data from step 2, update component state by using this.setState
App component detected there's an update on state, hence execute render method to load component again
Hence, you should definitely handle the case where this.state.TodoList has no data, which happened at first load
UPDATES:
component lifecycle componentWillMount is now deprecated which means you shouldn't be using it anymore. Replace it with componentDidMount instead. Functionally wise they should be no difference in your example
Initially, render method is called after cwm method. So console log shows the state's empty value first time.
But you have run an async operation in cwm method, so after it is done, the setstate method is called which causes the render method to run again.
Note: ComponentWillMount, componentWillUpdate and componentWillUpdate props method are deprecated.
You should move this API call to componentDidmount or ComponentDidUpdate method.
However, event after this, your console log will appear twice- one for initial render and second for setstate called after API call.
Note : componentWillMount is deprecated and will work until version 17.
Source - https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
Most suitable React Lifecycle for calling APIs:
componentDidMount is the most prefered method to perform asynchronous tasks like API calls, setTimeouts , etc..
It'll work as -
On componentDidMount your API gets called
As per lifecycle order, lastly render method will be called. (Still API hasn't returned response). Your UI is displayed at initial paint.
Once API gets response, you use this.setState() which will force a re-render operation.
Again your UI changes are updated
Remember : this.setState() will only be called once whether you have called it once or more than once in lifecycle method

React native redux loading state order

I want the getKey function to wait for the updated state from friendExist.
Currently getKey is set to false as the initial state however friendExist should set the boolean to "true". But it seems that "this.props.getKey(this.props.exist)" dosen't wait "this.props.friendExist(uid)" to update before it runs the code.
componentDidMount(){
const uid = 'MZdKuFpGmGRntb0nyF4PO0f6kco1';
this.props.friendExist(uid);
//friendExist should update this.props.exist to true
this.props.getKey(this.props.exist)
}
Both friendExist and getKey are actions that dispatch the new state.
Your function componentDidMount is a synchronous function, so it will execute inside code without waiting for any unrelated functions. Unless you change its type to asynchronous, and tell exactly how you want it to wait, this.props.exist will never be true at the time this.props.getKey is executed.
An alternative, simpler solution is taking the update of this.props.exist at componentWillReceiveProps, and execute this.props.getKey in there instead
componentDidMount(){
const uid = 'MZdKuFpGmGRntb0nyF4PO0f6kco1';
this.props.friendExist(uid);
}
componentWillReceiveProps(nextProps, nextState) {
if (nextProps.exist) {
this.props.getKey(nextProps.exist)
}
}
I assume you are using react-redux mapStateToProps() to get the updated state. This behavior is expected as react-redux uses react's setState() internally, which is asynchronous, meaning your state may not map to the props on the same time you dispatch the action.
I suggest creating a function getKeyIfFriendExist() instead which does the dispatching and checking of the latest state inside.

Resources