这个章节我们会把注意力集中到React程序的异常处理上,看看如何更加优雅的处理程序里出现的所有异常。
错误边界
这个是React16中新增的概念,错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获并处理发生在其子组件树任何位置的 JavaScript 错误,并且,我们也可以指定渲染出错误显示 UI。
错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为成错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。
错误边界无法捕获以下场景中产生的错误:
- 事件处理
- 异步代码
- 服务端渲染
- 错误边界自身抛出来的错误(并非它的子组件)
新增containers/ErrorBoundary.tsx
import * as React from 'react'
export interface ErrorBoundaryState {
hasError: boolean;
}
class ErrorBoundary extends React.Component<{}, ErrorBoundaryState> {
constructor(props: {}) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: any) {
return { hasError: true };
}
componentDidCatch(error: any, info: any) {
if (error) {
console.error(error)
}
if (info) {
console.log(info)
}
}
render() {
if (this.state.hasError) {
return 出错了
;
}
return this.props.children;
}
}
export default ErrorBoundary;
Redux异常捕获
错误边界中,对于事件处理和异步代码中的异常无能为力。所以我们需要针对这2个地方的异常做处理。触发事件及异步调用基本上都是通过
Action 来完成的。所以我们只要能够捕获 Action异常来做处理即可,这里推荐全局性的通过中间价来完成此处理。
新增middleware/index.ts
import { Store } from "redux";
import { Action } from "redux-actions";
// 捕获Redux Action异常的中间件
export const reduxCatch = (errorHandler: Function) => (store: Store) => (next: Function) => (action: Action<{}>) => {
try {
// 异步Action被标记为出错时手动抛出异常
if (action.error) {
throw action.payload;
}
return next(action);
} catch (err) {
// 自定义的异常处理函数
errorHandler(err, store.getState, action, store.dispatch);
return err;
}
}
初始化此中间件时,需要提供异常处理的函数,可以直接浏览器打印也可以接入Sentry 这种集中式日志管理系统。
接着修改 configureStore.ts
// 创建store
export const store = createStore(
// 跟reducer
rootReducer,
// 应用中间件
applyMiddleware(
thunk,
routerMiddleware(history),
loggerMiddleware,
reduxCatch((error: any, getState: any, lastAction: any, dispatch: any) => {
console.error(error);
})
)
)
然后运行程序查看,由于我们提供的初始化获取数据的url为随便写的 test ,如下所示
useEffect(() => {
let { dispatch } = props;
dispatch(searchNovels('test'))
}, [])
所以调用url找不到的异常被中间件捕获了。
而对于那些并没有触发调用Action 的事件处理不能通过该方式捕获,只能直接try catch 块获取。
let handleClick = () => {
try {
throw "11111111";
} catch (error) {
console.log(error)
}
}