I have a working app and am just trying to set the state to loading: true
while my app makes a submission call so I can show a loading screen. Because I want to make sure I set the state before making the loading call, I use a callback. However I'm not seeing my loading update with the code below:
submitSecurityAnswer = () => {
const { submit, handleError, navigate } = this.props;
const { answer } = this.state;
this.setState({ loading: true }, () => {
console.log('setState', this.state)
try {
submit({ answer, ...this.props }).then(({ errors, status }) => {
this.setState({ answer: '' });
if (status && status === 200) {
navigate();
} else if (status === 401) {
this.setState({ showError: true });
} else {
handleError(errors[0]);
}
});
} catch (err) {
console.log(err);
}
});
this.setState({ loading: false });
};
When I check my console log I see that the state has not updated and loading is still false.
What am I missing here?
I have a working app and am just trying to set the state to loading: true
while my app makes a submission call so I can show a loading screen. Because I want to make sure I set the state before making the loading call, I use a callback. However I'm not seeing my loading update with the code below:
submitSecurityAnswer = () => {
const { submit, handleError, navigate } = this.props;
const { answer } = this.state;
this.setState({ loading: true }, () => {
console.log('setState', this.state)
try {
submit({ answer, ...this.props }).then(({ errors, status }) => {
this.setState({ answer: '' });
if (status && status === 200) {
navigate();
} else if (status === 401) {
this.setState({ showError: true });
} else {
handleError(errors[0]);
}
});
} catch (err) {
console.log(err);
}
});
this.setState({ loading: false });
};
When I check my console log I see that the state has not updated and loading is still false.
What am I missing here?
Share Improve this question asked Oct 11, 2017 at 18:13 TurnipdabeetsTurnipdabeets 6,0159 gold badges45 silver badges65 bronze badges 5-
If I understood what you are trying to do correctly your
this.setState({loading:false})
should be right before or afternavigate();
– bennygenel Commented Oct 11, 2017 at 18:18 -
actually if you change
this.setState({ answer: '' });
tothis.setState({ answer: '', loading:false });
it should work too – bennygenel Commented Oct 11, 2017 at 18:18 -
yea, I'm trying to
this.setState({loading:false})
before thesubmit()
– Turnipdabeets Commented Oct 11, 2017 at 18:19 - then put it before the submit() function – bennygenel Commented Oct 11, 2017 at 18:19
-
You are calling
this.setState({ loading: true })
andthis.setState({ loading: false })
at the same time. The latter call kind of overrides the first one. – Felix Kling Commented Oct 11, 2017 at 18:23
2 Answers
Reset to default 4This is happening, because you are calling this.setState({ loading: false });
in the end of your function.
You are setting loading: true
and loading: false
at the same time. When you your callback is invoking, the last setState function is also changed state value to false.
You should set loading state to false after request was received:
submitSecurityAnswer = () => {
const { submit, handleError, navigate } = this.props;
const { answer } = this.state;
this.setState({ loading: true }, () => {
console.log('setState', this.state)
try {
submit({ answer, ...this.props }).then(({ errors, status }) => {
this.setState({ answer: '' });
if (status && status === 200) {
navigate();
} else if (status === 401) {
this.setState({ showError: true, loading: false });
} else {
handleError(errors[0]);
}
});
} catch (err) {
console.log(err);
}
});
};
That because you are setting loading
to false
before the async operation has pleted.
Just move this.setState({ loading: false });
inside the callback
;
Edit
As a followup to your ment:
I tried removing that but saw the same results
I didn't say to remove it just move it inside the callback of setstate
.
What happens is that you are setting it to true
then false
at the same call stack iteration and not waiting for the async operation to plete.
Consider this scenario which is similar to your code :
In this example i set the loading
back to false before the async operation (setTimeout
) is finished, hence i'm just overriding the previous state without waiting.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
message: ''
};
}
ponentDidMount() {
this.setState({ loading: true }, () => {
console.log(this.state);
setTimeout(() => {
this.setState({
message: 'we have data'
});
console.log(this.state);
}, 500);
});
this.setState({ loading: false });
}
render() {
const {message, loading} = this.state;
return (
<div>
{loading ? <div>Loading...</div> : <div>{message}</div>}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The fix will be to move the next state change inside the callback:
In this example i moved the line this.setState({ loading: false });
inside the callback after the async operation has pleted and this fixed the unwanted behavior.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
message: ''
};
}
ponentDidMount() {
this.setState({ loading: true }, () => {
console.log(this.state);
setTimeout(() => {
this.setState({
message: 'we have data'
});
this.setState({ loading: false });
}, 500);
});
}
render() {
const {message, loading} = this.state;
return (
<div>
{loading ? <div>loading...</div> : <div>{message}</div>}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>