错误处理

React 16 之前,组件在运行期间如果执行出错,就会阻塞整个应用的渲染,这时候只能刷新页面才能恢复应用。React 16 引入了新的错误处理机制,默认情况下,当组件中抛出错误时,这个组件会从组件树中卸载,从而避免整个应用的崩溃。这种方式比起之前的处理方式有所进步,但用户体验依然不够友好。React 16 还提供了一种更加友好的错误处理方式——错误边界(Error Boundaries)。错误边界是能够捕获子组件的错误并对其做优雅处理的组件。优雅的处理可以是输出错误日志、显示出错提示等,显然这比直接卸载组件要更加友好。

定义了 componentDidCatch(error, info) 这个方法的组件将成为一个错误边界,现在我们创建一个组件 ErrorBoundary:

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(err, info) {
    // 显示错误UI
    this.setState({ hasError: true });
    // 同时输出错误日志
    console.log(err, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Oops, something went wrong!</h1>;
    }
    return this.props.children;
  }
}

然后在 App 中使用 ErrorBoundary:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      user: { name: "react" }
    };
  }

  // 将user置为null,模拟异常
  onClick = () => {
    this.setState({ user: null });
  };

  render() {
    return (
      <div>
        <ErrorBoundary>
          <Profile user={this.state.user} />
        </ErrorBoundary>
        <button onClick={this.onClick}>更新</button>
      </div>
    );
  }
}

const Profile = ({user}) => <div>name: {user.name}</div>;

点击更新按钮后,Profile 接收到的属性 user 为 null,程序会抛出 TypeError,这个错误会被 ErrorBoundary 捕获,并在界面上显示出错提示。注意,使用 create-react-app 创建的项目,当程序发生错误时,create-react-app 会在页面上创建一个浮层显示错误信息,要观察 ErrorBoundary 的正确效果,需要先关闭错误浮层。

本节项目源代码的目录为 /chapter-03/react16-error-boundary 。