Infinite loop while calling react setState in a second component - reactjs

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.

Related

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.

DidComponentUpdate Infinite Loop React Native

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.

How to use shouldComponentUpdate() and componentWIllUpdate() method correctly in ReactJs?

Before i was using componentWillReceiveProps to update my component local object after getting data from redux.
componentWillReceiveProps(nextProps) {
if (nextProps.editUser && !nextProps.loading && !isEqual(this.state.user, nextProps.editUser)){
this.setState({user:{
name:nextProps.editUser.name,
email:nextProps.editUser.email,
}}, function(){
this.setState({addModalUserEdit:true});
});
}
}
But now i want to use shouldComponentUpdate and componentWillUpdate as in react documentation as per react life cycle.
shouldComponentUpdate(nextProps, nextState){
if (nextProps.editUser && !nextProps.loading && !isEqual(this.state.user, nextProps.editUser)){
return true;
}
return false;
}
componentWillUpdate(nextProps, nextState) {
if (nextProps.editUser && !nextProps.loading && !isEqual(this.state.user, nextProps.editUser)){
this.setState({user:{
name:nextProps.editUser.name,
email:nextProps.editUser.email,
}}, function(){
this.setState({addModalUserEdit:true});
});
}
}
but i generate an error as
"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."
Please guide me what i am doing wrong in understanding react life cycle.
Thanks in advance.
shouldComponentUpdate() is invoked before rendering when new props or state are being received.
shouldComponentUpdate (nextProps) {
return nextProps !== this.props
}
componentDidUpdate() is invoked immediately after update state.
Both method is not called for the initial render.
shouldComponentUpdate is run before the update to the state (and that is why it gives you the nextState as a property) so when doing test against the state you need to use the nextState.
shouldComponentUpdate(nextProps, nextState){
if (nextProps.editUser && !nextProps.loading && !isEqual(nextState.user, nextProps.editUser)){
return true;
}
return false;
}
Keep in mind though, that it is advised not to do deep equality checks in shouldComponentUpdate because it will hurt performance.
Additionally the componentWillUpdate is (in recent versions) being called UNSAFE_componentWillUpdate and it states
Note that you cannot call this.setState() here; nor should you do
anything else (e.g. dispatch a Redux action) that would trigger an
update to a React component before UNSAFE_componentWillUpdate()
returns.
The suggested method is to use the componentDidUpdate.
shouldComponentUpdate use to decide that changes in props or the state have affected to component output or not.
componentDidUpdate will be triggered when there is an update on component output.
shoudComponentUpdate take 2 arguments, nextProps and nextState. that contains a new update of props and state. and returning a boolean.

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.

How to setState the props value in ComponentDidUpdate in react-native?

componentDidUpdate(){
var date = this.props.navigation.state.params.selected_date
this.setState({
sleepinputs_date: date
})
}
When I try to setState the props value it throws an error "
You had created infinite state update.
Inside componentDidUpdate you update the state, when it updates the state, componentDidUpdate invokes again in this stuff keeps going without ending.
According to react docs, you will get an argument in componentDidUpdate. You can set state only if you use a conditional like the following.
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
Basically you are comparing the old props with the new ones. Only if they are different, you can keep updating or modifying the value. If previous props are the same as current props, why you botter to set state them again.

Resources