MST

星途 面试题库

面试题:React错误边界捕获异常信息后如何优雅地恢复应用状态

假设你已经在React应用中成功使用错误边界捕获到了子组件抛出的异常信息。在捕获异常后,应用可能处于一种不稳定状态。请阐述你会采取哪些策略来优雅地恢复应用状态,使得用户体验不受太大影响,并举例说明如何在代码层面实现这些策略。
16.9万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

策略

  1. 显示友好提示:向用户展示一个友好的错误提示,告知他们发生了错误,但应用正在努力恢复。这样可以避免用户因为看到空白页面或异常行为而感到困惑。
  2. 重置组件状态:尝试重置引发错误的组件到一个已知的稳定状态。例如,如果是一个表单组件抛出错误,可以将表单重置为初始状态。
  3. 重试机制:对于一些可能是临时性的错误,提供重试功能。比如网络请求失败导致的错误,用户可以点击重试按钮再次尝试操作。
  4. 回滚操作:如果错误发生在某个操作过程中,尝试回滚该操作,以确保应用状态的一致性。

代码示例

  1. 显示友好提示
    import React, { Component } from'react';
    
    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = {
          hasError: false
        };
      }
    
      componentDidCatch(error, errorInfo) {
        console.log('捕获到错误:', error, errorInfo);
        this.setState({ hasError: true });
      }
    
      render() {
        if (this.state.hasError) {
          return (
            <div>
              <p>很抱歉,发生了一个错误。我们正在努力恢复。</p>
            </div>
          );
        }
        return this.props.children;
      }
    }
    
    class ErrorProneComponent extends Component {
      render() {
        throw new Error('模拟错误');
        return <div>这部分不会渲染</div>;
      }
    }
    
    function App() {
      return (
        <ErrorBoundary>
          <ErrorProneComponent />
        </ErrorBoundary>
      );
    }
    
    export default App;
    
  2. 重置组件状态
    import React, { Component } from'react';
    
    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = {
          hasError: false
        };
      }
    
      componentDidCatch(error, errorInfo) {
        console.log('捕获到错误:', error, errorInfo);
        this.setState({ hasError: true });
      }
    
      render() {
        if (this.state.hasError) {
          // 重置子组件状态的逻辑,这里假设子组件有reset方法
          return (
            <div>
              <p>发生错误,正在重置...</p>
              {React.cloneElement(this.props.children, { reset: true })}
            </div>
          );
        }
        return this.props.children;
      }
    }
    
    class ErrorProneForm extends Component {
      constructor(props) {
        super(props);
        this.state = {
          value: ''
        };
      }
    
      handleChange = (e) => {
        this.setState({ value: e.target.value });
        if (this.state.value === 'error') {
          throw new Error('模拟表单错误');
        }
      };
    
      render() {
        if (this.props.reset) {
          this.setState({ value: '' });
        }
        return (
          <div>
            <input
              type="text"
              value={this.state.value}
              onChange={this.handleChange}
            />
          </div>
        );
      }
    }
    
    function App() {
      return (
        <ErrorBoundary>
          <ErrorProneForm />
        </ErrorBoundary>
      );
    }
    
    export default App;
    
  3. 重试机制
    import React, { Component } from'react';
    
    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = {
          hasError: false,
          error: null,
          errorInfo: null
        };
      }
    
      componentDidCatch(error, errorInfo) {
        console.log('捕获到错误:', error, errorInfo);
        this.setState({ hasError: true, error, errorInfo });
      }
    
      retry = () => {
        this.setState({ hasError: false, error: null, errorInfo: null });
      };
    
      render() {
        if (this.state.hasError) {
          return (
            <div>
              <p>发生错误: {this.state.error.message}</p>
              <button onClick={this.retry}>重试</button>
            </div>
          );
        }
        return this.props.children;
      }
    }
    
    class ErrorProneNetworkComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {
          data: null
        };
        this.fetchData = this.fetchData.bind(this);
      }
    
      componentDidMount() {
        this.fetchData();
      }
    
      fetchData() {
        // 模拟网络请求失败
        throw new Error('网络请求失败');
        // 实际中应该使用如fetch等进行网络请求
        // fetch('your - api - url')
        //  .then(response => response.json())
        //  .then(data => this.setState({ data }));
      }
    
      render() {
        return (
          <div>
            {this.state.data? (
              <p>数据: {JSON.stringify(this.state.data)}</p>
            ) : (
              <p>加载数据中...</p>
            )}
          </div>
        );
      }
    }
    
    function App() {
      return (
        <ErrorBoundary>
          <ErrorProneNetworkComponent />
        </ErrorBoundary>
      );
    }
    
    export default App;
    
  4. 回滚操作
    import React, { Component } from'react';
    
    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = {
          hasError: false
        };
      }
    
      componentDidCatch(error, errorInfo) {
        console.log('捕获到错误:', error, errorInfo);
        this.setState({ hasError: true });
        // 调用父组件的回滚函数,假设父组件传递了rollback方法
        if (typeof this.props.rollback === 'function') {
          this.props.rollback();
        }
      }
    
      render() {
        if (this.state.hasError) {
          return (
            <div>
              <p>发生错误,已回滚操作。</p>
            </div>
          );
        }
        return this.props.children;
      }
    }
    
    class ErrorProneTransactionComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {
          balance: 100
        };
        this.makeTransaction = this.makeTransaction.bind(this);
      }
    
      makeTransaction() {
        // 模拟交易操作,假设扣除50
        const newBalance = this.state.balance - 50;
        if (newBalance < 0) {
          throw new Error('余额不足');
        }
        this.setState({ balance: newBalance });
      }
    
      rollback() {
        this.setState({ balance: 100 });
      }
    
      render() {
        return (
          <div>
            <p>余额: {this.state.balance}</p>
            <button onClick={this.makeTransaction}>进行交易</button>
            <ErrorBoundary rollback={() => this.rollback()}>
              {/* 子组件进行可能导致错误的操作 */}
            </ErrorBoundary>
          </div>
        );
      }
    }
    
    function App() {
      return (
        <ErrorProneTransactionComponent />
      );
    }
    
    export default App;