我们知道,在使用 react 渲染页面的时候,如果某个子组件发生了错误,将会造成整个页面的白屏:
// page 组件,代表整个页面
class page extends React.Component {
// 如果 Component1 渲染失败,将会导致整个 page 组件渲染失败
render() {
return (
<div>
<Component1></Component1>
<Component2></Component2>
<Component3></Component3>
</div>
)
}
}
这样就会造成一个问题,有些子组件并没有那么重要,是否能够渲染出来,都不应该影响到整个页面。
为了解决这个问题,react 16 中提供了 componentDidCatch 方法,用于捕获组件中的渲染异常,这样当某个组件挂掉的时候,我们可以做针对性的处理,不至于整个页面瘫痪掉。
componentDidCatch的使用方式
react 官方提供了一个示例:demo
包装组件问题
从上面的示例可以得知,我们要想处理某个组件的渲染异常,需要做两件事,首先申明一个错误处理组件:
class ErrorBoundary 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;
}
}
然后包装我们需要处理的组件:
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
一旦使用了这种包装方式,我们必须人工判断哪些组件应该做错误处理,哪些组件可以不做错误处理,这使得我们的业务代码里会混入很多错误处理的非业务代码:
<ErrorBoundary>
<A>
<B></B>
<ErrorBoundary>
<C></C>
</ErrorBoundary>
<D><E></E></D>
</A>
</ErrorBoundary>
是否可以让我们编程的时候,能够专注于业务,摆脱这种包装代码呢?我们可以尝试下面的方式。
重写 React.createElement
我们可以通过重写 React.createElement ,将代码中的每个组件都包装一下:
const rc = React.createElement
React.createElement = (type, config, ...other) => {
return rc(ErrorBoundary, keyObj, rc(type, config, ...other))
}
使用重写后的 React.createElement 效果:
<A>
<B></B>
<C></C>
<D><E></E></D>
</A>
// 上面代码通过重写的 React.createElement 转化后变为:
<ErrorBoundary>
<A>
<ErrorBoundary><B></B></ErrorBoundary>
<ErrorBoundary><C></C></ErrorBoundary>
<ErrorBoundary>
<D>
<ErrorBoundary><E></E></ErrorBoundary>
</D>
</ErrorBoundary>
</A>
</ErrorBoundary>
这样就可以统一处理渲染异常,而不会侵入我们的业务代码!
网友评论