Change state using componentDidUpdate() - reactjs

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.

Related

Why I can't set the state from inside componentDidUpdate even with a condition?

I'm comparing props from within componentDidUpdate and trying to update the state and, after that, fetch some data (which depends of some state params).
componentDidUpdate(prevProps) {
if (prevProps.location.search.split("?description=")[1] !==
this.props.location.search.split("?description=")[1]) {
this.setState({
searchParams: this.getInitialSearchParams(),
isLoaded: false,
entities: []
})
this.fetchMore()
}
}
But, as the question suggests, when I'm going to fetch the data, the function is using a previous state.
Sandbox: https://codesandbox.io/s/0y8cm
To evaluate the error do the following: use the navbar to search one of the entities. Check the console to see the state. Do another search in the navbar to the same entity. Check the console again and see the unchanged state.
What am I doing wrong?
Thanks a lot!
This is because setState is asynchronous and the value is not set immediately after the call. You should use its callback to follow up.
setState(updater, [callback]);
This is useful for when you want to use the state values right after updating them. So instead you would have:
componentDidUpdate(prevProps) {
if (prevProps.location.search.split("?description=")[1] !==
this.props.location.search.split("?description=")[1]) {
this.setState({
searchParams: this.getInitialSearchParams(),
isLoaded: false,
entities: []
}, this.fetchMore); //or () => this.fetchMore() if you want to send params
}
}
You can read more about setState here: https://reactjs.org/docs/react-component.html#setstate
It's very useful to read about the setState and React's lifecycle as well.
Hope this was helpful.

set the state through a function in reactjs

I am checking a condition and if that condition holds true then through function a want to change the state. i tries but i console it is still showing false only. it will be really helpful if you suggest good way to to it,
this.state = {
popUpStatus:false,
}
handlePopUp = () => {
this.setState({
popUpStatus: true,
});
}
if(a > 100){
this.handlePopUp;
console.log(popUpStatus)l // false
}
this.setState() is an asynchronous call. Therefore, You should not check this.state.popUpStat value right after calling this.setState().
The correct way to do so is passing a callback function as a second parameter in this.setState() call like
this.setState({
popUpStatus: true,
},()=>console.log(this.state.popUpStatus))
setState function does not update the state immediately. It just enqueues the changes. popUpStatus will have a different value once the component is re-rendered.
"setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. Think of setState() as a request rather than an immediate command to update the component....."
You can read more on setState here https://reactjs.org/docs/react-component.html#setstate
To test it, you can log it in your render function, and check how it updates.
You have to display this.state.popUpStatus
Replace console.log(popUpStatus) with console.log(this.state.popUpStatus)
Try to do this:
this.handlePopUp();
console.log(this.state.popUpStatus);
But consider that setState is async function so you have to do this:
this.setState({
popUpStatus: true,
}, ()=> { console.log(this.state.popUpStatus); });
Perhaps with hooks should be better:
useEffect(()=> { setPopUpStatus(a>100); }, [a])

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.

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