【发布时间】:2016-04-27 17:57:36
【问题描述】:
当在 React 中渲染一个组件(包含许多子组件)并且由于某种原因引发 JS 错误时,最好的处理方法是什么?当然我可以捕捉到错误,但最终你想要渲染的东西可能无法捕捉到,因为缺少所需的信息。您可以在 propTypes 中使用 .isRequired 提前进行验证,但这只是控制台在失败时发出的警告。
【问题讨论】:
-
我曾经回答过关于反应中的错误处理here
标签: reactjs
当在 React 中渲染一个组件(包含许多子组件)并且由于某种原因引发 JS 错误时,最好的处理方法是什么?当然我可以捕捉到错误,但最终你想要渲染的东西可能无法捕捉到,因为缺少所需的信息。您可以在 propTypes 中使用 .isRequired 提前进行验证,但这只是控制台在失败时发出的警告。
【问题讨论】:
标签: reactjs
React 16 引入了一个名为 “错误边界” 的新概念来处理 React 组件内部发生的错误,而不会破坏整个应用程序。
错误边界是捕获 JavaScript 错误的 React 组件 在其子组件树中的任何位置,记录这些错误并显示 后备 UI 而不是崩溃的组件树。
错误边界组件已通过新的生命周期方法componentDidCatch(error, info) 成为可能。与其他生命周期方法不同,只有在渲染期间、生命周期方法或组件的任何子(包括所有孙子)组件的构造函数中发生任何错误时,才会调用此方法。
在代码中,错误边界组件将如下所示。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Error occurred!</h1>;
}
return this.props.children;
}
}
我们可以将这个错误边界组件用作我们代码中的普通组件。
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
现在如果 MyComponent 在渲染、生命周期方法或构造过程中抛出任何 JavaScript 错误,错误边界组件的 componentDidCatch 将触发并更改状态以显示 Error occurred! 消息而不是损坏的 MyComponent。
这个新功能带来了另一个重要的含义,在迁移到 React 16 之前你想知道它。以前,如果发生错误,它会留下损坏的 UI,即使它通常不会按预期工作,直到你重新加载页面。但是,在 React 16 中,如果错误没有被任何错误边界处理,它将导致整个应用程序的卸载。
因此,您适当地为应用添加错误边界将为您的用户提供更好的体验。因此,即使 UI 的某些区域已经崩溃,用户也可以与您的应用程序的一部分进行交互。
请参阅React official documentation on error boundaries 或this official blog post 了解更多信息。它们几乎涵盖了您需要了解的有关此新功能的所有信息。
【讨论】:
理想情况下,您不应该有任何会导致 js 错误的东西。不过,我们并不生活在一个完美的世界中,所以我会做一些事情来帮助减轻它们。
想想你正在编写的代码会如何破坏
如果你在一个变量上调用一个方法,想一想,“这总是这种数据类型吗?”
handleClick = (e) => {
e && e.preventDefault()
// code here
}
handleSomething = (data) => {
if (Array.isArray(data)) {
data.reduce(...) // we know this will be here
}
}
创建变量时,使用默认值。
意思是const { loading = false, data = [] } = this.props。这将有助于数据类型的一致性。
更优雅地处理异步数据
确保处理组件的挂起/等待状态。您可以(例如)使用 Loading 组件在加载时呈现内容。
render() {
const { data = [] } = this.props
return (
<Loading loading={data.length > 0}>
<MyComponent data={data} />
</Loading>
)
}
加载组件的位置。
const Loading = ({ children, loading = false, message = 'Loading...' }) => {
if (loading) {
return <span>{message}</span>
}
return children
}
你应该捕捉异常
如果您正在编写您认为可能会破坏的内容,或者您只是想过于谨慎,您可以使用try catch 块来捕获功能组件中的异常。
const MyComponent = ({ data = [] }) => {
try {
return <ul>{data.map( item => <li key={item}>{item}</li> )</ul>
} catch (error) {
const message = error.message || 'I broke!'
return <span className="broken-component">{message}</span>
}
}
在类组件中你可以使用
componentDidCatch(error, errorInfo) {
// Handle error here.
}
The docs have a ton of great info if you'd like to learn more
现在,这只是描述我正在谈论的内容的一个基本示例。但这将有助于减少您的 JS 异常,这些异常(当未捕获时)会破坏您的单页应用程序。所以你需要处理它们。
如果 URL 无效,则重定向到 404 页面。如果组件需要数据,则等待数据呈现它。如果您尝试访问嵌套对象的属性(特别是如果它来自服务器),又名this.props.myData.obj.something.somethingelse。很有可能该对象路径不会一直存在。您需要确保其中的每一个都不是未定义的或为空的。
额外功劳
我使用了 lodash 的 get 助手,它帮助我减少了异常!
_.get(this.props, 'myData.obj.something.somethingelse') // returns `undefined` if the path is invalid
我想这个故事的寓意是你应该在你的代码中积极主动地捕捉可能破坏的东西。 :)
【讨论】:
catch,然后是dispatch({type: NOTIFY_FAILURE...}),reducer 更新它的信息,然后在某个地方显示。
对于 React v16 及更高版本,推荐的方法是使用错误边界。 React 团队的这个页面(简短而好)解释了这个特性: https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html。它还包含一个 codepen 演示的链接。
【讨论】:
部分 UI 中的 JavaScript 错误不应破坏整个应用程序。为了为 React 用户解决这个问题,React 16 引入了“错误边界”的新概念。
错误边界是 React 组件,它在其子组件树的任何位置捕获 JavaScript 错误,记录这些错误,并显示一个备用 UI 而不是崩溃的组件树。错误边界在渲染期间、生命周期方法中以及它们下方的整个树的构造函数中捕获错误。
如果一个类组件定义了一个名为componentDidCatch(error, info)的新生命周期方法,它就会成为错误边界
【讨论】: