菜鸟教程小白 发表于 2022-12-12 14:32:46

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


                                            <p><p>我正在尝试使用 React 的 <code>componentDidCatch</code> 方法来捕获导致我的 Cordova iOS 应用程序崩溃的错误。它似乎可以防止崩溃,但该方法从未被调用,所以我不知道是什么导致了问题。</p>

<p>我的组件:</p>

<pre><code>import styled from &#39;styled-components&#39;;
import { View, observable } from &#39;shared/View&#39;;
import { Player } from &#39;video-react&#39;;
import SpinnerOverlay from &#39;shared/components/SpinnerOverlay&#39;;
import * as Icons from &#39;shared/components/Icons&#39;;
import fs from &#39;fileSystem&#39;;

export default class VideoOverlay extends View {
@observable ready = false;

handlePlayerStateChange = (state, prevState) =&gt; {
    const { onClose } = this.props;

    if(state.paused &amp;&amp; !prevState.paused &amp;&amp; onClose) {
      try {
      onClose();
      } catch(error) {
      console.log(&#39;--- onClose error&#39;, error);
      }
    }
};

handlePlayerRef = (player) =&gt; {
    player.subscribeToStateChange(this.handlePlayerStateChange);
    player.play();
};

async componentDidMount() {
    try {
      const { mediaFile } = this.props;
      await mediaFile.download(fs);
      this.ready = true;
    } catch(error) {
      console.log(&#39;--- SHIT&#39;, error);
    }
}

componentDidCatch(error) {
    // Needed because `Player` throws an error when unmounting.
    // This method doesn&#39;t get called (not sure why), but without this method,
    // the app crashes when a video is closed.
    console.log(&#39;Caught&#39;, error);
}

render() {
    const { ready } = this;
    const { mediaFile } = this.props;
    const src = ready &amp;&amp; mediaFile.fileSrc;

    return (
      &lt;React.Fragment&gt;
      &lt;SpinnerOverlay visible/&gt;
      {ready &amp;&amp;
          &lt;Player ref={this.handlePlayerRef}&gt;
            &lt;source src={src}/&gt;
          &lt;/Player&gt;
      }
      &lt;/React.Fragment&gt;
    );
}
}
</code></pre>

<p>组件工作正常,但卸载时,它始终抛出此错误:</p>

<pre><code>2018-02-22 12:52:19.706530-0800 App ERROR: The above error occurred in the &lt;Player&gt; 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
</code></pre>

<p>此错误出现在 XCode 控制台中,上面没有错误。 <code>componentDidCatch</code> 中的 <code>console.log</code> 永远不会运行。 <code>componentDidMount</code> 和 <code>handlePlayerStateChange</code> 中的 <code>catch</code>block 也永远不会到达。</p>

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

<p>另外,我无法在浏览器中重现此问题,因为此组件仅适用于 iOS(此组件利用 iOS 自动全屏自动播放视频)。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>问题是 <code>componentDidCatch</code> 不会捕获事件处理程序错误,就像您的 <code>handlePlayerRef</code> 方法中的处理程序会发生的错误一样。那是因为这些类型的错误不会在组件的渲染过程中发生,这是 <code>didCatch</code> 的预期目的。</p>

<p>例如,这不会被 <code>didCatch</code> 正确捕获和记录:</p>

<pre><code>class MyComponent extends Component {
componentDidCatch() {
    console.log(&#39;whats wrong?&#39;)
}

onButtonClick() {
    //errors emerging here
}

render() {
    return (
      &lt;button onClick={this.onButtonClick}&gt;Click&lt;/button&gt;
    );
}
}
</code></pre>

<p>如果您需要捕获此类错误,请使用 <code>handlePlayerRef</code> 中的常规 try/catchblock 。</p>

<pre><code>handlerPlayerRef() {
try {
    player.subscribeToStateChange(this.handlePlayerStateChange);
    player.play();
} catch () {
    console.error(&#39;Something wrong happened&#39;);
}
}
</code></pre>

<p>此外,通常最好将组件渲染错误与组件功能分离,因为它往往会产生难以调试的神秘错误。</p>

<p>您是否尝试过构建一个 ErrorBoundary 组件来包裹您的 VideoOverlay 类?这样做并将其呈现条件为不存在错误:</p>

<pre><code>class ErrorBoundary extends React.Component {
componentDidCatch(err) {
    this.setState({
      hasError: true
    });
}

render() {
    return this.state.hasError
      ?&lt;h2&gt;Oh noes! Something went wrong.&lt;/h2&gt;
      :this.props.children
   }
}
</code></pre>

<p>现在您可以只提供 VideoOverlay 作为 ErrorBoundary 的子级:</p>

<pre><code>&lt;ErrorBoundary&gt;
&lt;VideoOverlay /&gt;
&lt;/ErrorBoundary&gt;
</code></pre>

<p>详细阅读 <a href="https://reactjs.org/docs/error-boundaries.html" rel="noreferrer noopener nofollow">here</a> </p></p>
                                   
                                                <p style="font-size: 20px;">关于javascript - React`componentDidCatch` 未被调用但需要处理错误?,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/48937080/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/48937080/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: javascript - React `componentDidCatch` 未被调用但需要处理错误?