• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

javascript - React `componentDidCatch` 未被调用但需要处理错误?

[复制链接]
菜鸟教程小白 发表于 2022-12-12 14:32:46 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我正在尝试使用 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 &&
          <layer 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 <layer> 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 控制台中,上面没有错误。 componentDidCatch 中的 console.log 永远不会运行。 componentDidMounthandlePlayerStateChange 中的 catch block 也永远不会到达。

最奇怪的是,如果我删除 componentDidCatch,应用程序会因上述错误而崩溃,因此 componentDidCatch 似乎在做某事 ;只是没有让我真正处理错误。

另外,我无法在浏览器中重现此问题,因为此组件仅适用于 iOS(此组件利用 iOS 自动全屏自动播放视频)。



Best Answer-推荐答案


问题是 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 block 。

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

关于javascript - React `componentDidCatch` 未被调用但需要处理错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48937080/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap