DidComponentUpdate Infinite Loop React Native - reactjs

Why DidComponentUpdate is on Infinite Loop?
I trying to get some data from an Api When user changeText on Input
componentDidUpdate(prevState){
if (prevState.userinput !== this.state.userinput){
fetch('https://'+this.region+'.api.riotgames.com/lol/summoner/v4/summoners/by-name/'+this.state.userinput+'?api_key='+this.apikey+'RGAPI-484c0156-6203-4611-b281-c3933b6ac175')
.then(respostauser => respostauser.json())
.then(json => this.setState({user : json}));
fetch('https://'+this.region+'.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-summoner/'+this.state.user.accountId+'?api_key='+this.api_key)
.then(respostamastery => respostamastery.json())
.then(json => this.setState({usermastery : json}));
this.x ++;}
}

As stated in the react docs here, setting state inside the componentDidUpdate lifecycle method must be done using some conditional check(s), else you'd encounter an infinite loop.
This is because by default, an update to state causes the componentDidUpdate method to be invoked. In your snippet above, this lifecycle method contains logic that would unconditionally update state (this.setState), hence the infinite loop.

Related

componentDidMount is being fired multiple times in ReactJs [duplicate]

This question already has answers here:
ReactJs componentDidMount executes twice
(2 answers)
Closed 3 months ago.
I am calling an api using axios inside componentDidMount and the api is being called multiple times.
Here is my code
export default class Listings extends Component{
state = {
listings: null
}
//when the component is mounted to dom first
componentDidMount() {
this.getAllCryptoListings();
}
getAllCryptoListings() {
let response = null;
axios.get('http://localhost:8000/')
.then( (res) => {
response = res;
console.log(response.data.data);
})
.catch( (error) => console.log(error) )
.finally( () => { this.setState({listings: response.data.data}) } )
}
}
Normally I expected the code to run one time only as per the description of the function here componentDidMount.
The documentation says
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.
How to make the code inside componentDidMount run only once?
componentDidMount() is called twice if you setState in it, you won't be able to disable this.
You can make your call in componentWillMount, but this is UNSAFE so be careful using it! (not recommended)
If this does not work you should check where your component is called. Maybe his parents are re-rendering and so calling the render to verify twice with the initial state.
You can read more about it here
In React version 18, a change was made to strict mode so that components will mount, then unmount, then mount again. This was added to help us all start catching issues that will affect an upcoming feature. In a future version of react, the state will be able to be preserved between unmounts, and as a result, components may mount multiple times.

Change state using componentDidUpdate()

Hi i'm trying to change a state using componentDidUpdate() but I get an infinite loop.
componentDidUpdate(prevState) {
if (prevState.selectedOption1 !== this.state.selectedOption1) {
this.setState({ disabled2: false });
}
}
I can´t figure it out
The componentDidUpdate function uses 3 parameters:
previous properties
previous state
snapshot (for when getSnapshotBeforeUpdate() is used.
You are treating the first parameter as previous state, which it is not, this results in your condition always being true and creating your infinite loop as setState is continually called.
To fix the issue correctly use the parameters of componentDidUpdate
componentDidUpdate(
prevProps, // This is the previous properties
prevState // This is the previous state.
) {
if (prevState.selectedOption1 !== this.state.selectedOption1) {
this.setState({ disabled2: false });
}
}
When you change the state, the component updates.
Thus, if you change state state when the component updates then you get your infinite loop.
If you insist on keeping this behavior then you might want to add a boolean check to your state.

Infinite loop while calling react setState in a second component

I am working with 3 components that pass data to one another. On the second
component I am trying to do this when a flag passFlag is true.
cancelCourse = () => {
this.setState({
settledAmount:'',
creditAmount:'',
productDescription:'',
reasonCode:'',
storedCollectedInformation:[]
});
}
componentDidUpdate(){
if(this.props.passFlag === true){
this.cancelCourse();
}
}
I kept getting an infinite loop with the error message below.
invariant.js:42 Uncaught Invariant Violation: Maximum update depth exceeded.
This can happen when a component repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
How can I fix this please?
All I am trying to do is to reset the controls on a form to empty.
try to change the condition. something like this.
componentDidUpdate(prevProps){
if(this.props.passFlag === true && this.props.passFlag != prevProps.passFlag){
this.cancelCourse();
}
}
let me know if this worked
The problem with componentDidUpdate() here is that it calls this.cancelCourse(), which changes the state, and then it calls componentDidUpdate() again and the cycle repeats.
You could do something with componentWillReceiveProps(), if I understood what you are trying to correctly:
componentWillReceiveProps(nextProps) {
if (nextProps.passFlag !== this.props.passFlag) {
this.cancelCourse();
}
}
Be aware that componentWillReceiveProps() will be deprecated in the future.

Why in React componentWillReceiveprops fires before setState() in componentDidMount?

I have been programming with React for a while now but I have never faced this annoying issue, in one of my components componentWillReceiveProps fires before setState() in componentDidMount gets executed. This causes several issues in my application.
I have a variable this.props.flag received from props which is going to be stored in the state of the component:
componentDidMount() {
if (!_.isEmpty(this.props.flag)) {
console.log('Flag Did:', this.props.flag);
this.setState({
flag: this.props.flag
},
() => doSomething()
);
}
In my componentWillReceiveProps method the variable this.state.flag is going to be replaced just if it is empty or if it different from the value of this.props.flag (the checks are made by using the lodash library):
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
Suppose that the prop flag in this case has always the same value and that this.state.flag is initialized to undefined. When I check the console log I see the following result:
Flag Did: true
Flag Will: true undefined true
Therefore when the code enters componentWillReceiveProps the value of this.state.flagis still undefined, that means has not been set yet by the setState in componentDidMount.
This is not consistent with React lifecycle or am I missing something? How can I avoid such behaviour?
ComponentWillReceiveProps() will be called in each update life-cycle caused by changes to props (parent component re-rendering). Since Javascript is synchronous you might have validate props sometimes to save app crashes. I've not totally understood the context of your app but what you can do is:
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
if(!flag){
return;,
}
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
You can return if state is undefined. It will be called again upon parent re-rendering. But this might not be use-case.
Anyways you should look into this:
But I can think of at least 1 (maybe theoretical) scenario where the order will reversed:
Component receives props, and starts rendering. While component is
rendering, but has not yet finished rendering, component receives new
props. componentWillReceiveProps() is fired, (but componentDidMount
has not yet fired) After all children and component itself have
finished rendering, componentDidMount() will fire. So
componentDidMount() is not a good place to initialise
component-variables like your { foo: 'bar' }. componentWillMount()
would be a better lifecycle event. However, I would discourage any use
of component-wide variables inside react components, and stick to
design principles:
all component variables should live in either state or props (and be
immutable) all other variables are bound by the lifecycle method (and
not beyond that)
As suggested by user JJJ, given the asynchronous nature of setState, the check if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) in componentWillReceiveProps is executed before setState inside componentDidMount executes flag: this.props.flag. The order of the operations is:
Code enters componentDidMount.
Code executes setState in
componentDidMount (flag: this.props.flag hasn't happened yet).
Code exits componentDidMount, setState in componentDidMount is
still under execution (flag: this.props.flag hasn't happened yet).
Component receive new props, therefore enters
componentWillReceiveProps.
The statement if
(!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) in
componentWillReceiveProps is executed (this.state.flag is still
undefined).
Code finishes the execution of setState inside
componentDidMount and sets flag: this.props.flag and executes
doSomething().
Code finishes the execution of setState inside
componentWillMount and sets flag: nextProps.flag and executes
doSomething().
Given the asynchronous nature of setState, 6 and 7 could be executed in parallel and therefore we do not know which one will finish its execution first. DoSomething() in this case is potentially called at least 2 times when it must be called once instead.
In order to solve these issues, I changed my code this way:
componentWillReceiveProps(nextProps) {
if (!_.isEmpty(nextProps.flag) && !_.isEqual(this.props.flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
This way I compare the new version(nextProps) with the old version(this.props) of the props, without waiting for the flag value to be stored in the component's state.

Difference between componentWillMount and contructor in react? [duplicate]

As far as I could see, the only thing a componentWillMount can do and a constructor cannot is to call setState.
componentWillMount() {
setState({ isLoaded: false });
}
Since we have not called render yet, a setState in componentWillMount will prepare the state object before we enter the first render() pass. Which is essentially the same thing a constructor does:
constructor(props) {
super(props);
this.state = { isLoaded: false };
}
But I see another use case where componentWillMount is useful (on server side).
Let's consider something asynchronous:
componentWillMount() {
myAsyncMethod(params, (result) => {
this.setState({ data: result });
})
}
Here we cannot use the constructor as assignment to this.state won't trigger render().
What about setState in componentWillMount? According to React docs:
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
So, here I think React will use the new state value for the first render and avoids a re-render.
Question 1: Does this means, inside componentWillMount, if we call setState in an async method's callback (can be a promise callback), React blocks initial rendering until the callback is executed?
Having this setup on client-side (yes I see that use case in server-side rendering), if I assume the above is true, I will not see anything until my asynchronous method completes.
Am I missing any concepts?
Question 2: Are the any other use cases that I can achieve with componentWillMount only, but not using the constructor and componentDidMount?
Does this means, inside componentWillMount, if we call setState in an
async method's callback (can be a promise callback), React blocks
initial rendering until the callback is executed?
No, see here.
The following code doesn't block render (bear in mind this would be an anti pattern anyways to call setState there)
componentWillMount: function() {
new Promise((resolve, reject) => {
setTimeout(()=> {
resolve();
}, 2000)
}).then(() => this.setState({ promiseResult: 'World' }));
},
Question 2: Are the any other use cases that I can achieve with
componentWillMount only, but not using the constructor and
componentDidMount?
No, for ES6 classes you can discard componentWillMount. It is only needed if you use React.createClass({... })
EDIT: Apparently, I'm wrong. Thanks to #Swapnil for pointing this out. Here is the discussion.
React throws a warning if there is a side effect in the constructor which modifies state in another component, because it assumes that setState in the constructor itself and potentially during render() is being called. So no side effects in the constructor are desired.
This is not the case if you do it in componentWillMount, no errors are thrown. On the other hand, the guys from facebook discourage side effects in componentWillMount also. So if you don't have any side effects, you could use the constructor instead of componentWillMount. For side effects it is recommended to use componentDidMount instead of componentWillMount.
Either way, you don't need componentWillMount.

Resources