Update April 2020:
The issue seems to be fixed in latest React 16.13.1, see this sandbox example. Thanks to @abernier for pointing this out.
I have made some research, and I have found one important difference:
React does not process errors from async lifecycle methods.
So, if you write something like this:
componentDidMount()
{
throw new Error('I crashed!');
}
then your error will be caught by the error boundry, and you can process it and display a graceful message.
If we change the code like this:
async componentDidMount()
{
throw new Error('I crashed!');
}
which is equivalent to this:
componentDidMount()
{
return Promise.reject(new Error('I crashed!'));
}
then your error will be silently swallowed. Shame on you, React...
So, how do we process errors than? The only way seems to be explicit catch like this:
async componentDidMount()
{
try
{
await myAsyncFunction();
}
catch(error)
{
//...
}
}
or like this:
componentDidMount()
{
myAsyncFunction()
.catch(()=>
{
//...
});
}
If we still want our error to reach the error boundary, I can think about the following trick:
- Catch the error, make the error handler change the component state
- If the state indicates an error, throw it from the
render
method
Example:
class BuggyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
buggyAsyncfunction(){ return Promise.reject(new Error('I crashed async!'));}
async componentDidMount() {
try
{
await this.buggyAsyncfunction();
}
catch(error)
{
this.setState({error: error});
}
}
render() {
if(this.state.error)
throw this.state.error;
return <h1>I am OK</h1>;
}
}