美文网首页JavaScript 进阶营React.js让前端飞
在 React 16 中使用高阶组件来捕获异常

在 React 16 中使用高阶组件来捕获异常

作者: 枫上雾棋 | 来源:发表于2017-08-08 10:55 被阅读0次

    可能你已经知道, 在 React 16 中, 将会引进一个全新的架构 - React Fiber, 它彻底重写了 React 的协调算法, 并引入了一些新的特性. 这篇文章就是跟大家分享 React 16 中新的生命周期方法 - componentDidCatch, 它能捕获在子组件树中任何地方的 JavaScript 异常,并打印这些错误和展示备用UI, 就像将 children 包裹在一个大的 try/catch 语句块中. 你可以阅读 Dan Abramov 的 Error Handling in React 16 获取更多关于 componentDidCatch 的内容.

    绝大多数的开发人员使用 React 16 都应该是由 15 升级上来的, 然而, 为了使用错误处理而去重写整个组件肯定是不明智的, 那么, 该怎么办呢, 当然有更好的处理方式, 那就是想办法封装组件, 像修改组件定义那是逼上梁上的行为.

    那么, 错误处理究竟可以干些什么

    • 当有错误发生时, 我们可以友好地展示 fallback 组件
    • 避免 normalfallback 组件耦合
    • 我们可以清楚地发现某些服务的一些报错
    • 我们可以复用报错fallback

    接下来, 我们看一个最森破的的实例

    class ErrorHandler extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      componentDidCatch(error, info) {
        // Display fallback UI
        this.setState({ hasError: true });
        // You can also log the error to an error reporting service
          logErrorToMyService(error, info);
      }
    
      render() {
        if (this.state.hasError) {
          // You can render any custom fallback UI
          return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
      }
    }
    
    // Then you can use it as a regular component
    const MyPage = props => (
      <Container>
        <ErrorHandler>
          <MyComponent />
    
          <MyOtherComponent />
        </ErrorHandler>
      </Container>
    )
    

    其实, 我们还可以将其优化一下, 比如将 ErrorHandler 参数化, 将 reportErrorToService 作为 props 传递, 用 component 替代 上面返回的几行视图代码. 所以, 我们还是要更新我们的组件定义, 以便将 UI 的一部分添加 ErrorHandler 中. 但正如我们之前所讨论的, 我们并不想修改组件的定义, 我们只 decorate 它们

    对此, 我们可以使用高阶组件

    const MyFallbackComponent = props => (
      <h1>Something Went Wrong</h1>
    )
    
    // we'll talk about `withErrorHandler` later
    const MyErrorHandler = withErrorHandler(
      reportErrorToService,
      MyFallbackComponent
    )
    
    const MyComponent = MyErrorHandler(props => (
      /* ... */
    ))
    
    const MyOthercomponent = MyErrorHandler(props => (
      /* ... */
    ))
    
    const MyPage = props => (
      <Container>
        <MyComponent />
    
        <MyOtherComponent />
      </Container>
    )
    

    我们可以使用 withErrorHandler HOC 来封装组件, 使组件获得错误处理, 即当错误发生时, 调用 reportErrorToService 并展示 fallback 组件, 从而, 我们帅气地避免了大量组件的定义修改

    然而, 我们还可以更帅气. 当服务报错跟 fallback 组件的视图都基本相同时, 我们可以像下样一样 export withErrorHandler HOC

    import withErrorHandler from 'error-handler-hoc'
    
    import reportErrorToService from '../services/errorReporter'
    import FallbackView from '../components/Fallback/'
    
    export default withErrorHandler(
      reportErrorToService,
      FallbackView
    )
    

    然后, 我们 export 封装过后的组件就可以了

    // MyComponent.jsx
    import ErrorHandler from '../HOCs/ErrorHandler.js'
    
    const MyComponent = props => (
      /* ... */
    )
    
    export default ErrorHandler(MyComponent)
    
    // ====================
    // MyOtherComponent.jsx
    import ErrorHandler from '../HOCs/ErrorHandler.js'
    import ...
    
    const MyOtherComponent = props => (
      /* ... */
    )
    
    // you might already be using HOCs
    export default compose(
      SomeOtherHOC,
      ErrorHandler
    )(MyOtherComponent)
    
    // ====================
    // MyPage.jsx
    import { MyComponent, MyOtherComponent } from './components'
    
    const MyPage = () => (
      <Container>
        <MyComponent />
        <MyOtherComponent />
      </Container>
    )
    

    这样, 我们就可以轻松地给组价添加错误处理, 同样, 我们也可以轻松地移除

    那么, withErrorHandler 究竟是如何工作的呢, 其实, 实现它还是挺简单的

    function withErrorHandler (errorCallback, FallbackComponent, Component) {
      class WithErrorHandler extends React.Component {
        constructor () {
          super()
    
          // Construct the initial state
          this.state = {
            hasError: false,
            error: null,
            errorInfo: null
          }
        }
    
        componentDidCatch (error, info) {
          // Update state if error happens
          this.setState({ hasError: true, error, errorInfo: info })
    
          // Report errors
          errorCallback(error, info, this.props)
        }
    
        render () {
          // if state contains error we render fallback component
          if (this.state.hasError) {
            const { error, errorInfo } = this.state
            return (
              <FallbackComponent
                {...this.props}
                error={error}
                errorInfo={errorInfo}
              />
            )
          }
    
          return <Component {...this.props} />
        }
      }
      WithErrorHandler.displayName = `withErrorHandler(${Component.displayName})`
      return WithErrorHandler
    }
    

    总结

    这个高阶组件可以用来封装任何组件, 捕获所有异常, 你可以用其封装整个 page, 也可以用其封装个别组件.

    点击该 repository 可以查看更多源码

    原文链接: Catching exceptions using Higher Order Components in React 16

    相关文章

      网友评论

        本文标题:在 React 16 中使用高阶组件来捕获异常

        本文链接:https://www.haomeiwen.com/subject/xqctrxtx.html