React Error Boundary 从不工作

thc*_*ark 5 javascript error-handling reactjs

我有一个组件,它在其渲染方法中生成错误。

在树的更高位置,组件被connect()分配react-redux到用于获取数据的资源,我还有其他 HOCS 等等(注入类等)。

该组件使用错误边界以防(例如)提供给它的资源中缺少属性(常见问题)。

我提炼了最小的例子,访问 undefined 的属性来重现相同的错误:

import React from 'react'

// An Error Boundary which returns either a fallback component (if supplied to props) or (default) an empty div, instead of screwing up the whole tree
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      hasError: false,
    }
  }

  static getDerivedStateFromError(error) {
    return { hasError: true }
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true })
    console.log('do stuff with', error, info)
  }

  render() {
    console.log('RENDERING ERROR BOUNDARY WITH STATE:', this.state)
    if (this.state.hasError) {
      if (this.props.fallback) {
        return (this.props.fallback)
      }
      return (<div />)
    }
    return this.props.children
  }
}

class MyComponent extends React.Component {
  render() {
    const resource = undefined
    return (
      <ErrorBoundary>
        <p>{resource.missingProperty}</p>
      </ErrorBoundary>
    )
  }
}

export default MyComponent
Run Code Online (Sandbox Code Playgroud)

当然,尝试渲染组件会生成以下堆栈跟踪:

Uncaught TypeError: Cannot read property 'missingProperty' of undefined
    at MyComponent.render (index.jsx:58)
    at finishClassComponent (react-dom.development.js:15141)
    at updateClassComponent (react-dom.development.js:15096)
    at beginWork (react-dom.development.js:15980)
    at performUnitOfWork (react-dom.development.js:19102)
    at workLoop (react-dom.development.js:19143)
    at HTMLUnknownElement.callCallback (react-dom.development.js:147)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
    at invokeGuardedCallback (react-dom.development.js:250)
    at replayUnitOfWork (react-dom.development.js:18350)
    at renderRoot (react-dom.development.js:19261)
    at performWorkOnRoot (react-dom.development.js:20165)
    at performWork (react-dom.development.js:20075)
    at performSyncWork (react-dom.development.js:20049)
    at requestWork (react-dom.development.js:19904)
    at scheduleWork (react-dom.development.js:19711)
    at Object.enqueueSetState (react-dom.development.js:12936)
    at Connect.push../node_modules/react/cjs/react.development.js.Component.setState (react.development.js:356)
    at Connect.onStateChange (connectAdvanced.js:205)
    at Object.notify (Subscription.js:23)
    at Subscription.notifyNestedSubs (Subscription.js:62)
    at Connect.onStateChange (connectAdvanced.js:202)
    at Object.notify (Subscription.js:23)
    at Subscription.notifyNestedSubs (Subscription.js:62)
    at Connect.onStateChange (connectAdvanced.js:202)
    at Object.dispatch (createStore.js:175)
    at e (<anonymous>:1:40553)
    at asyncDispatchMiddleware.js:33
    at middleware.js:13
    at middlewareCamel.js:13
    at middlewareDate.js:34
    at _callee$ (middleware.js:285)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:290)
    at Generator.prototype.<computed> [as next] (runtime.js:116)
    at step (asyncToGenerator.js:21)
    at asyncToGenerator.js:32

The above error occurred in the <MyComponent> component:
    in MyComponent (created by Connect(MyComponent))
    in Connect(MyComponent) (at withCachedResource.js:43)
    in _class (created by StreamField)
    in div (at streamfield.js:17)
    in StreamField (at StoryDrawer.js:34)
    in div (created by Grid)
    in Grid (created by WithStyles(Grid))
    in WithStyles(Grid) (at GridItem.jsx:20)
    in GridItem (created by WithStyles(GridItem))
    in WithStyles(GridItem) (at StoryDrawer.js:33)
    in div (created by Grid)
    in Grid (created by WithStyles(Grid))
    in WithStyles(Grid) (at GridContainer.jsx:20)
    in GridContainer (created by WithStyles(GridContainer))
    in WithStyles(GridContainer) (at StoryDrawer.js:26)
    in StoryDrawer (created by WithStyles(StoryDrawer))
    in WithStyles(StoryDrawer) (created by Connect(WithStyles(StoryDrawer)))
    in Connect(WithStyles(StoryDrawer)) (at Map.js:264)
    in div (created by Paper)
    in Paper (created by WithStyles(Paper))
    in WithStyles(Paper) (created by Drawer)
    in Transition (created by Slide)
    in EventListener (created by Slide)
    in Slide (created by WithTheme(Slide))
    in WithTheme(Slide) (created by Drawer)
    in RootRef (created by Modal)
    in div (created by Modal)
    in Portal (created by Modal)
    in Modal (created by WithStyles(Modal))
    in WithStyles(Modal) (created by Drawer)
    in Drawer (created by WithStyles(Drawer))
    in WithStyles(Drawer) (at Map.js:252)
    in div (at Map.js:153)
    in Map (created by WithStyles(Map))
    in WithStyles(Map) (created by Connect(WithStyles(Map)))
    in Connect(WithStyles(Map)) (created by AddUrlProps(Connect(WithStyles(Map))))
    in AddUrlProps(Connect(WithStyles(Map))) (at MapPage.jsx:24)
    in div (at FullArea/index.js:43)
    in FullArea (at MapPage.jsx:22)
    in div (at MapPage.jsx:21)
    in MapPage (created by WithStyles(MapPage))
    in WithStyles(MapPage) (created by Connect(WithStyles(MapPage)))
    in Connect(WithStyles(MapPage)) (created by Route)
    in Route (at App.js:33)
    in Switch (at App.js:25)
    in div (at App.js:23)
    in App (at src/index.js:32)
    in RouterToUrlQuery (at src/index.js:31)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (at src/index.js:30)
    in Provider (at src/index.js:29)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit ... to learn more about error boundaries.
Run Code Online (Sandbox Code Playgroud)

超级奇怪的是 ErrorBoundaryrender()方法从不打印到控制台。就好像那个包装器不存在一样,react 完全忽略它,只直接渲染孩子。我不知道这怎么可能。

我已将其推送到生产服务器上,并且在那里出现相同的情况,因此我认为这与开发与生产配置无关。

问题

  1. 我要疯了吗?我是否完全误解了 React Error Boundaries 的用途?

  2. 调用堆栈上方的某些东西是否会以某种方式干扰错误处理程序?

来自@thedude的解决方案

伙计们,用你的投票对接受的答案表示爱意。更正的解决方案(现在我明白了)是产生错误的组件必须被包装,而不仅仅是组件定义的元素。更正的代码:

import React from 'react'

class MyComponent extends React.Component {
  render() {
    const resource = undefined
    return (
      <p>{resource.missingProperty}</p>
    )
  }
}

class MyBoundedComponent extends React.Component {
  render() {
    return (
      <ErrorBoundary>
        <MyComponent />
      </ErrorBoundary>
    )
  }
}

export default MyBoundedComponent
Run Code Online (Sandbox Code Playgroud)

the*_*ude 8

ErrorBoundry永远不会渲染,因为渲染的方法MyComponent永远不会返回。

每次渲染都会发生错误。

render 在 react 中生成了要由 react 使用的元素树的表示,您造成的错误会使此过程短路。

您可以通过将错误边界包装在MyComponent不在其内部(在呈现MyComponent