Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runtime): 修复 react 框架用户自定义 componentDidCatch 无效的问题 #8191

Closed

Conversation

digiaries
Copy link
Contributor

这个 PR 做了什么? (简要描述所做更改)

增加了 React 框架中对应用开发者自定义的 componentDidCatch 的处理逻辑。

这个 PR 是什么类型? (至少选择一个)

这个 PR 满足以下需求:

  • 提交到 master 分支
  • Commit 信息遵循 Angular Style Commit Message Conventions
  • 所有测试用例已经通过
  • 代码遵循相关包中的 .eslintrc, .tslintrc, .stylelintrc 所规定的规范
  • 在本地测试可用,不会影响到其它功能

这个 PR 涉及以下平台:

  • 微信小程序
  • 支付宝小程序
  • 百度小程序
  • 头条小程序
  • QQ 轻应用
  • 快应用平台(QuickApp)
  • Web 平台(H5)
  • 移动端(React-Native)

其它需要 Reviewer 或社区知晓的内容:

@Chen-jj
Copy link
Contributor

Chen-jj commented Dec 11, 2020

@digiaries componentDidCatch 测试了是可以正常工作的。

React 中规定 componentDidCatch 不能捕获当前组件的错误。也就是页面组件设置了 componentDidCatch,也只能捕获子组件的渲染错误,而页面组件自身的错误会往上冒泡,到你 PR 中改动到的包裹组件中。

包裹组件默认添加了 componentDidCatchgetDerivedStateFromError,假设某页面的组件树渲染错误,只会这个页面白屏,其它页面不受影响。

总结来说,

默认情况下 Taro 会自动设置错误边界,令页面错误不至于令程序崩溃。

要手动捕获页面子组件树的错误,把页面组件设置为错误边界即可。

要手动捕获包括页面组件自身的错误,书写一个错误边界组件,然后包裹页面组件即可,这样也符合 React 的使用规范:

class Index extends Component {
  componentDidMount () {
    throw Error('xxx')
  }
  render () {
    return (
      <View>index</View>
    )
  }
}

export default class ErrorBoundary extends Component {
  static getDerivedStateFromError () {
    return {
      hasError: true
    }
  }

  state = {
    hasError: false
  }

  componentDidCatch (err) {
    console.log('error: ', err)
  }

  render () {
    return this.state.hasError ? <View>something error</View> : <Index />
  }
}

@digiaries
Copy link
Contributor Author

@digiaries componentDidCatch 测试了是可以正常工作的。

React 中规定 componentDidCatch 不能捕获当前组件的错误。也就是页面组件设置了 componentDidCatch,也只能捕获子组件的渲染错误,而页面组件自身的错误会往上冒泡,到你 PR 中改动到的包裹组件中。

包裹组件默认添加了 componentDidCatchgetDerivedStateFromError,假设某页面的组件树渲染错误,只会这个页面白屏,其它页面不受影响。

总结来说,

默认情况下 Taro 会自动设置错误边界,令页面错误不至于令程序崩溃。

要手动捕获页面子组件树的错误,把页面组件设置为错误边界即可。

要手动捕获包括页面组件自身的错误,书写一个错误边界组件,然后包裹页面组件即可,这样也符合 React 的使用规范:

class Index extends Component {
  componentDidMount () {
    throw Error('xxx')
  }
  render () {
    return (
      <View>index</View>
    )
  }
}

export default class ErrorBoundary extends Component {
  static getDerivedStateFromError () {
    return {
      hasError: true
    }
  }

  state = {
    hasError: false
  }

  componentDidCatch (err) {
    console.log('error: ', err)
  }

  render () {
    return this.state.hasError ? <View>something error</View> : <Index />
  }
}

这里的问题是如果用这个包裹,会导致一些像小程序的分享等方面的功能异常,所以希望是直接在页面上启用而不是额外再包裹一层。

@Chen-jj
Copy link
Contributor

Chen-jj commented Dec 14, 2020

@digiaries 使用了 HOC 后,小程序页面生命周期不触发的确是一个问题。

但感觉还是应该遵循 React 的规范,页面组件的错误能在页面组件中捕获的做法有点 hack。

建议页面组件,写成函数式组件,小程序页面生命周期的 hook 是能生效的。

// index.js
function Index () {
  useShareAppMessage(() => {
    return {
      title: 'xxx'
    }
  })
  return (
    <View>
      index
    </View>
  )
}

export default class ErrorBoundary extends Component {
  static getDerivedStateFromError (error) {
    return {
      hasError: true
    }
  }

  state = {
    hasError: false
  }

  componentDidCatch (err) {
    console.log('error: ', err)
  }

  render () {
    return this.state.hasError ? <View>something error</View> : <Index />
  }
}

页面配置要记得配一下开启分享:

// index.config.js
export default {
  navigationBarTitleText: '首页',
  enableShareAppMessage: true
}

@zousandian
Copy link

@Chen-jj 每个页面都要单独写 error boundary 吗?能否在全局添加?

@Chen-jj
Copy link
Contributor

Chen-jj commented Apr 7, 2021

@Chen-jj 每个页面都要单独写 error boundary 吗?能否在全局添加?

可以封装为一个 HOC 吧

@Chen-jj Chen-jj closed this Apr 7, 2021
@tomnattle
Copy link

`
class IndexPage extends Taro.Component {

...

render() {
throw new Error("调试用途");
const threshold = 20;
return (


...

}
`
ErrorBoundary可以捕获子组件的错误,已验证,但是所有的pages都是一级组件,该如何捕获?
例如上方的代码中IndexPage出现的异常,该怎么捕获?

@Chen-jj
Copy link
Contributor

Chen-jj commented Jun 2, 2021

@digiaries componentDidCatch 测试了是可以正常工作的。

React 中规定 componentDidCatch 不能捕获当前组件的错误。也就是页面组件设置了 componentDidCatch,也只能捕获子组件的渲染错误,而页面组件自身的错误会往上冒泡,到你 PR 中改动到的包裹组件中。

包裹组件默认添加了 componentDidCatchgetDerivedStateFromError,假设某页面的组件树渲染错误,只会这个页面白屏,其它页面不受影响。

总结来说,

默认情况下 Taro 会自动设置错误边界,令页面错误不至于令程序崩溃。

要手动捕获页面子组件树的错误,把页面组件设置为错误边界即可。

要手动捕获包括页面组件自身的错误,书写一个错误边界组件,然后包裹页面组件即可,这样也符合 React 的使用规范:

class Index extends Component {
  componentDidMount () {
    throw Error('xxx')
  }
  render () {
    return (
      <View>index</View>
    )
  }
}

export default class ErrorBoundary extends Component {
  static getDerivedStateFromError () {
    return {
      hasError: true
    }
  }

  state = {
    hasError: false
  }

  componentDidCatch (err) {
    console.log('error: ', err)
  }

  render () {
    return this.state.hasError ? <View>something error</View> : <Index />
  }
}

@tomnattle 把页面组件使用错误边界包裹一层,像上述回答一样

@soeasyjx
Copy link

有属性可以关闭Taro 自身自动设置错误边界,我不想要它

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
V-3 Version - 3.x
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants