我尝试使用React的componentDidCatch
方法来捕获导致我的Cordova iOS应用程序崩溃的错误。它似乎可以防止崩溃,但是方法永远不会被调用,所以我不知道是什么导致了这个问题。
我的组件:
import styled from 'styled-components';
import { View, observable } from 'shared/View';
import { Player } from 'video-react';
import SpinnerOverlay from 'shared/components/SpinnerOverlay';
import * as Icons from 'shared/components/Icons';
import fs from 'fileSystem';
export default class VideoOverlay extends View {
@observable ready = false;
handlePlayerStateChange = (state, prevState) => {
const { onClose } = this.props;
if(state.paused && !prevState.paused && onClose) {
try {
onClose();
} catch(error) {
console.log('--- onClose error', error);
}
}
};
handlePlayerRef = (player) => {
player.subscribeToStateChange(this.handlePlayerStateChange);
player.play();
};
async componentDidMount() {
try {
const { mediaFile } = this.props;
await mediaFile.download(fs);
this.ready = true;
} catch(error) {
console.log('--- SHIT', error);
}
}
componentDidCatch(error) {
// Needed because `Player` throws an error when unmounting.
// This method doesn't get called (not sure why), but without this method,
// the app crashes when a video is closed.
console.log('Caught', error);
}
render() {
const { ready } = this;
const { mediaFile } = this.props;
const src = ready && mediaFile.fileSrc;
return (
<React.Fragment>
<SpinnerOverlay visible/>
{ready &&
<Player ref={this.handlePlayerRef}>
<source src={src}/>
</Player>
}
</React.Fragment>
);
}
}
该组件工作正常,除非卸载时,它始终抛出此错误:
2018-02-22 12:52:19.706530-0800 App[1094:598984] ERROR: The above error occurred in the <Player> component:
in Player (created by Component)
in Component (created by Component)
in div (created by Screen)
in Screen (created by Component)
in Component (created by inject-Component-with-store)
in inject-Component-with-store (created by Route)
in Route (created by Component)
in Switch (created by Component)
in Component (created by inject-Component-with-store)
in inject-Component-with-store (created by Route)
in Route (created by Component)
in Component (created by Route)
in Route (created by withRouter(Component))
in withRouter(Component) (created by inject-withRouter(Component)-with-api)
in inject-withRouter(Component)-with-api (created by Component)
in Switch (created by Component)
in div (created by App__Root)
in App__Root (created by Component)
in Component (created by Route)
in Route (created by withRouter(Component))
in withRouter(Component)
in Router (created by HashRouter)
in HashRouter
in Provider
此错误出现在XCode控制台中,上面没有错误。 console.log
中的componentDidCatch
永远不会运行。 <{1}}和catch
中的componentDidMount
块也永远不会到达。
最奇怪的是,如果我删除handlePlayerStateChange
,应用程序会因上述错误而崩溃,因此componentDidCatch
似乎在做某事;它只是不让我真正处理错误。
此外,我无法在浏览器中重现此问题,因为此组件仅适用于iOS(此组件利用iOS自动全屏显示自动播放的视频)。
答案 0 :(得分:3)
事情是componentDidCatch
没有捕获事件处理程序错误,就像handlePlayerRef
方法中的处理程序一样。这是因为这些类型的错误在组件呈现期间不会发生,这是didCatch
的预期目的。
例如,didCatch
无法正确捕获和记录此内容:
class MyComponent extends Component {
componentDidCatch() {
console.log('whats wrong?')
}
onButtonClick() {
//errors emerging here
}
render() {
return (
<button onClick={this.onButtonClick}>Click</button>
);
}
}
如果您需要捕获这些类型的错误,请使用handlePlayerRef
内的传统try / catch块。
handlerPlayerRef() {
try {
player.subscribeToStateChange(this.handlePlayerStateChange);
player.play();
} catch () {
console.error('Something wrong happened');
}
}
此外,通常最好将组件渲染错误与组件功能分离,因为它往往会产生难以调试的神秘错误。
您是否尝试构建一个ErrorBoundary组件来包装您的VideoOverlay类?这样做并将其呈现为不存在错误:
class ErrorBoundary extends React.Component {
componentDidCatch(err) {
this.setState({
hasError: true
});
}
render() {
return this.state.hasError
? <h2>Oh noes! Something went wrong.</h2>
: this.props.children
}
}
现在您可以将VideoOverlay作为ErrorBoundary的子项提供:
<ErrorBoundary>
<VideoOverlay />
</ErrorBoundary>
详细阅读here