如何使用ErrorBoundary和componentDidCatch创建处理程序错误组件

时间:2019-03-13 17:00:09

标签: javascript reactjs

我发现自己试图使用ErrorBoundary创建错误处理程序,但是经过几天和大量的耐心,我设法通过在后端调用API来捕获错误并将错误保存在数据库中。 / p>

注意:如果您有更好的解决方案,请评论此帖子。

首先:按照ReactJs documentation的说明创建您的ErrorBoundary组件,但要添加一些修改

一旦您输入ComponentDidCatch,ErrorBoundary直接发送到渲染器,它就不会经历React的另一个生命周期,然后,我们创建了另一个中间组件来调用API并能够安静地使用生命周期,但是添加了一个回调方法来通知errorBoundary错误已被通知,并重定向主视图或主视图。

首先:我的Errorboundary组件:

 /**
 * @desc Dependencias
 */
import React, { Component, Fragment } from 'react';

/**
 * @desc Acciones
 */
import { saveError } from '../../actions/ErrorsActions';

/**
 * @desc Componentes
 */
import Modal from '../Modal/ModalContainer';
import ErrorMessage from './errorMesage';

class ErrorBoundary extends Component {

  /**
   * @desc Constructor del componente.
   * 
   * @param { Object } props 
   * 
   * @return { Void }
   */
  constructor(props) {

    super( props );

    // Estado incial.
    this.state = { 

      // Se encontro un error.
      foundError: {
        display : false,
        message: null
      }
    };
  }

  /**
   * @desc Ciclo de vida: Comienzo del montado del componente.
   * 
   * @return { void }
   */
  componentDidMount(){

    // Global catch of unhandled Promise rejections:
    global.onunhandledrejection = error => {  

      // Warning: when running in "remote debug" mode (JS environment is Chrome browser),
      // this handler is called a second time by Bluebird with a custom "dom-event".
      // We need to filter this case out:
      if (error instanceof Error) {

        // Alias
        let { foundError } = this.state;

        // Indice del split al stack.
        let index = error.stack.indexOf(".");

        // Asignamos el stack
        foundError.display = true;
        foundError.message = error.stack.substring( 0, index )
                                        .trim()
                                        .replace(/(\r\n|\n|\r)/gm, "");

        // Actualizamos el estado con el error.
        this.setState({ foundError })        

      }
    };
  }

  /**
   * @desc Detecta los errores no manipulados de la aplicación.
   * 
   * @param { Object } error 
   * @param { Object } info 
   * 
   * @return { Void }
   */
  componentDidCatch( error, info) {

    // Alias
    let { foundError } = this.state;

    // Indice del split al stack.
    let index = info.componentStack.indexOf("(");

    // Mensaje del error
    let message = error.message + " - " + info.componentStack.substring( 0, index ).trim();

    // Asignamos el stack
    foundError.display = true;
    foundError.message = message;

    // Actualizamos el estado con el error.
    this.setState({ foundError })

  }

  /**
   * @desc 
   * 
   * @return { void }
   */
  async closeModal(){

    // `enter code here`Alias
    let { foundError } = this.state;

    // Reiniciamos el estado del error.
    foundError.display = false;
    foundError.message = null;

    // Actualizamos el estado
    await this.setStateAsync({ foundError });

  }

  /**
   * @desc Renderiza el componente.
   * 
   * @return { * }
   */
  render() {

    // Alias
    let { language, children } = this.props;
    let { foundError } = this.state;

    // Validamos si se debe mostrar el modal de crash.
    if( !foundError.display )
      return children;
    else
      // You can render any custom fallback UI
      return ( <ErrorMessage { ...this.props }
                             onClose={ () => this.closeModal() }
                             stack={ foundError.stack } 
                             message={ foundError.message } /> );
  }
}

export default ErrorBoundary;

在渲染器中,我们检查是否存在错误,如果存在,则将其称为错误组件,如果没有,则返回子级。

在ErrorMessage组件中:

/**
 * @desc Dependencias
 */
import React, { Component, Fragment } from 'react';

/**
 * @desc Lenguaje
 */
import Lenguaje from '../../lenguage/es-ar.json';

/**
 * @desc Acciones
 */
import { saveError } from '../../actions/ErrorsActions';

/**
 * @desc Componentes
 */
import Modal from '../Modal/ModalContainer';

class ErrorMeesage extends Component {


  /**
   * @desc Constructor del componente.
   * 
   * @param { Object } props 
   * 
   * @return { Void }
   */
  constructor(props) {

    super( props );

    this.state = {

      // Modal de crash.
      modalCrash: {
        display: true,
        type: 'crash',
        content: {
          title: '',
          body: '',
        }
      }

    }

  }

  componentDidMount(){

    // Lanzamos el almacenamiento del stack.
    this.saveAudit();

  }

  /**
    * @desc Permite el acceso asincronico al setState de react.
    *
    * @param { Object } state
    * 
    * @return { Promise }
  */
  setStateAsync( state ){
   return new Promise( resolve => this.setState( state,  () => {
     resolve()
    }))
  }

  /**
   * @desc Envia para almacenar el stack de errores.
   * 
   * @return { Promise<void> }
   */
  async saveAudit(){

    // Alias
    let { history, country, message } = this.props;

    // Datos.
    let data = {
      type: 'WEB',
      subtype: 'ERROR',
      details: message
    };

    // Solicitamos el guardado del error.
    let response =  saveError( country, data );

    // Esperamos la solicitud se cumpla. 
    response.payload = await response.payload;

    // Datos del modal.
    let title       = Lenguaje && Lenguaje.WAS_AN_ERROR,
        body        = Lenguaje.GENERAL_ERROR_CODE.replace('{0}', response.payload.data ),
        callback    = async () => {

          await this.hideModalCrash();

          // Redirección.
          history.push('/home');

        };

    // Mostramos el modal de crash.
    await this.showModalCrash( title, body, callback ); 

  }

  /**
  * Muestra el modal de error.
  * 
  * @param { String } title
  * @param { String } body
  * 
  * @return { Promise<Boolean> }
  */
  async showModalCrash( title = '', body = '', callback = null){

  // Alias
  let { modalCrash } = this.state;

  try{

    // Asignamos los valores al modal.
    modalCrash.display = true;
    modalCrash.content.title = title; 
    modalCrash.content.body = body;
    modalCrash.callback = typeof callback === "function" && callback;

    // Actualizamos el estado interno.
    this.setState({ modalCrash });

    return true;

  }catch( error ){

    return false;

  }

  }

  /**
  * Oculta el modal de error.
  * 
  * @return { Promise<Boolean> }
  */
  async hideModalCrash(){

    // Alias
    let { modalCrash } = this.state;

    try{

      // Limpiamos el modal de eliminación.
      modalCrash.display = false;
      modalCrash.content.title = '';
      modalCrash.content.body = '';

      // Actualizamos el estado.
      await this.setStateAsync({ modalCrash });

      // El modal fue ocultado.
      await this.props.onClose();

      return true;

    }catch( error ){

      return false;

    }

  }

  /**
   * @desc Renderiza el componente.
   * 
   * @return { * }
   */
  render() {

    // Alias
    let { modalCrash } = this.state;
    let { language } = this.props;

    return(<Modal  type="crash"
                    isWrapper 
                    dataModal={ modalCrash } 
                    language={ language }
                    handleClick={ () => typeof modalCrash.callback === "function" && modalCrash.callback() } />);

  }

}
export default ErrorMessage;

在ErrorMessage组件中,我们调用API来保存错误,我们显示了通知用户的模式,然后在HideCrashModal中,我们有一个方法通知ErrorBoundary通知了错误:await this.props.onClose ();

有关如何实现此组件的示例:

  <Route path="/home"    render= { props => 
            <ErrorBoundary { ...this.props }> 
              <Welcome history={ props.history } dispatch={ props.dispatch } />  
            </ErrorBoundary>
            } />

现在,ComponentDidCatch无法捕获异步代码中的错误,但是我们用BlueBird替换了promise,我们可以使用在ErrorBoundary中捕获这些错误的方法,以便我们有一个集中的错误点,因此我们添加了全局变量。如果BlueBird错误处理程序以某种承诺检测到错误,它将以这种方式在ComponentDidMount生命周期中进行onunhandlerrejection。

0 个答案:

没有答案