Reputation: 1832
Lets say I have a login function
login = () => {
let url = baseURL + '/user/login?_format=json';
let data = {
"name": this.state.email,
"pass": this.state.password
};
return axios({
url,
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
withCredentials: true,
credentials: 'same-origin'
})
.then(function(result) {
console.log('result:', result);
this.setState({csrfToken: result.data.csrf_token});
this.setState({logoutToken: result.data.logout_token});
return result;
})
.catch(error => console.log('error:', error));
};
I then want to call the function onSubmit in React like follows. If the function returns an error for any reason. How do I prevent the next function in this case api.login()
from being run?
{api => (
<Form
onSubmit={async e => {
e.preventDefault();
await this.login();
api.login()
}}
>
<input/>
</Form>
Does a try/catch make sense in this case? I've tried several options including an inline try catch and the function runs no matter what happens as soon as the promise from this.login();
returns either an result or error.
Upvotes: 0
Views: 98
Reputation: 222354
This is the problem that is mentioned in the answer to the previous question,
The problem with login is that its control flow is flawed. It isn't be able to efficiently catch errors because it suppresses them.
.catch(error => console.log('error:', error))
suppresses the error, while it shouldn't for proper control flow. Rejections should be handled at top level where a promise is consumed. Even if an error needs to be handled in catch
(it's not necessary that console.log
is needed at all), it should be rethrown.
Consistent handling of asynchronous errors is a separate concern in React. Asynchronous errors need to be caught and rethrown synchronously in lifecycle hooks (likely componentDidUpdate
):
componentDidUpdate() {
if (this.state && this.state.error) {
throw this.state.error;
}
}
onSubmit = async e => {
try {
e.preventDefault();
await this.login();
api.login();
} catch (err) {
this.setState({ error: err });
}
}
render() {
...
<Form onSubmit={this.onSubmit}>
<input/>
</Form>
...
}
The error that is rethrown in componentDidUpdate
will propagate up to error boundary component or will result in exception, a demo.
Some additional helpers may be introduced to DRY try {...} catch (err) { this.setState({ error: err }) }
boilerplate up.
Upvotes: 2
Reputation: 724
Why not put api.login()
inside the first login promise then
callback?
login = () => {
let url = baseURL + '/user/login?_format=json';
let data = {
"name": this.state.email,
"pass": this.state.password
};
return axios({
url,
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
withCredentials: true,
credentials: 'same-origin'
})
.then(function(result) {
console.log('result:', result);
this.setState({csrfToken: result.data.csrf_token});
this.setState({logoutToken: result.data.logout_token});
api.login() // <----- if you want to check result just wrap it in an if statement if (result) api.login()
return result;
})
.catch(error => console.log('error:', error));
};
Otherwise you can make login()
to return a boolean or a truthy/falsy value then do something like this (untested code):
{api => (
<Form
onSubmit={async e => {
e.preventDefault();
await this.login() && api.login()
}}
>
<input/>
</Form>
Upvotes: 1
Reputation: 146
I think this might be happening because you're just running a console.log in your catch method, instead of throwing an error or rejecting a Promise. So the try/catch block for your await continues to run as if all is OK. Try throwing an error with Promise.reject or new Error().
var catchWithoutRejection = async () => {
await console.log('hello')
console.log('running')
}
catchWithoutRejection();
// hello
// running
// Promise {<resolved>: undefined}
var catchWithRejection = async () => {
await Promise.reject("hello")
console.log('not running')
}
catchWithRejection();
// Promise {<rejected>: "hello"}
Upvotes: 1