React - componente DidCatch no captura errores lanzados por componente infantil

Tengo un componente de clase padre con componentDidCatch método que me gustaría utilizar para registrar errores de frontend a backend.

componentDidCatch(error, errorInfo) {
    // Catch errors in any components below and log error message
    console.log(error)
    console.log(errorInfo)
    this.errorLoggGateway.loggError(`${error.toString()}, ${errorInfo.componentStack}`);
  }

También he implementado mi propio onUnhandledRejection método que escucha unhandledrejection eventos:

componentDidMount() {
    window.addEventListener('unhandledrejection', this.onUnhandledRejection);



onUnhandledRejection = (event) => {
    const exception = event.reason;
    console.log(event)
    if (process.env.NODE_ENV !== 'test') console.log('exception unhandled', exception);
    if (exception instanceof HttpError) {
      event.preventDefault();
      this.setState({
        errorMessages: this.generateHttpErrorMessages(exception)
      });
      return;
    }

    if (exception instanceof TypeError && exception.message === 'Failed to fetch') {
      event.preventDefault();
      this.setState({
        errorMessages: {
          user: 'Fetch failed',
          admin: 'Problems with network or API, please contact IT-service'
        }
      });
      return;
    }

    if (exception instanceof Error && exception.name === 'AbortError') {
      event.preventDefault();
      this.setState({
        errorMessages: {
          user: 'Request timeout fail.',
          admin: `Request timeout fail, please contact IT-service`
        }
      });
      return;
    }
    this.errorLoggGateway.loggError(exception.message);
  };

Este es el método de renderización:

render() {
    if (!this.state.hasLoaded) {
      return ;
    }
    return (
      <>
         this.rebootApp()}/>
        
           }/>
            this.login(fnr)} />}
          />
          
                          {
                            ({currentTime}) =>
                              
                          }
                          
                        }
          />
           } />
          
        
      
    );
  }

Si echo un error del método en un componente infantil, en este caso ExamRootPage:

handleAnswer = (question, answer) => {
    throw new Error('Crashed');

Tengo la pantalla de React usual con un error:

Error: Crashed
ExamRootPage.handleAnswer
C:/Users/myuser/Desktop/Projects/project/frontend/src/pages/ExamRootPage.js:193
  190 | };
  191 | 
  192 | handleAnswer = (question, answer) => {
> 193 |   throw new Error('Crashed');

¿Por qué no? componentDidCatch ni el onUnhandledRejection ¿No ha cogido ese error? ¿Cómo puedo arreglar esto para poder manejar esos errores allí? Estoy usando la versión React ^16.12.0.

Pregunta hecha hace 3 años, 5 meses, 0 días - Por logicloom


2 Respuestas:

  • El problema que estás experimentando es que el método componentDidCatch y el onUnhandledRejection no pueden capturar los errores lanzados por eventos síncronos como el throw en tu componente ExamRootPage.

    Dado que componentDidCatch se enfoca en capturar errores específicamente de los componentes hijos durante el renderizado, y onUnhandledRejection se enfoca en capturar promesas rechazadas no manejadas, ambos no son directamente capaces de interceptar errores lanzados de manera síncrona como en tu caso.

    Para manejar estos tipos de errores de manera más global, tienes algunas opciones:

    • Utilizar un bloque try-catch en el método handleAnswer: La forma más sencilla de capturar errores lanzados de manera síncrona es envolver el código problemático con un bloque try-catch en el método `handleAnswer` de `ExamRootPage`.

    Por ejemplo:

    javascript
    handleAnswer = (question, answer) => {
        try {
            throw new Error('Crashed');
        } catch (error) {
            // Manejar el error aquí
            // Por ejemplo, contar con el método componentDidCatch
            this.componentDidCatch(error, { componentStack: 'ExamRootPage.handleAnswer' });
        }
    }
    
    • Utilizar un gestor de errores de nivel superior: Puedes implementar un gestor de errores de nivel superior en tu aplicación que se encargue de capturar todos los errores no manejados. Este gestor de errores podría ser un componente de React que envuelve toda tu aplicación y captura cualquier excepción no controlada.

    Por ejemplo:

    javascript
    class ErrorBoundary extends React.Component {
      state = { hasError: false };
    
      componentDidCatch(error, errorInfo) {
        this.setState({ hasError: true });
        // Logear o manejar el error aquí
      }
    
      render() {
        if (this.state.hasError) {
          // Renderizar un componente de error personalizado
          return ;
        }
    
        return this.props.children;
      }
    }
    

    Donde utilizas este componente alrededor de tu aplicación principal:

    javascript
    
      
    
    

    Estas estrategias te permitirán capturar y manejar errores lanzados de manera síncrona como el caso que mencionas en tu componente ExamRootPage. ¡Espero que esto te ayude a resolver el problema!

    Respondida el Dec 18, 2020 a las 15:04 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • React 16 utiliza un modo de renderizado asincrónico, lo que significa que los errores lanzados durante el renderizado no siempre son capturados por componentDidCatch. Para manejar estos errores, puedes utilizar el método del ciclo de vida componentDidMount para agregar un controlador de errores al ciclo de eventos de JavaScript:

    javascript
    componentDidMount() {
      window.addEventListener('error', this.handleError);
    }
    
    componentWillUnmount() {
      window.removeEventListener('error', this.handleError);
    }
    
    handleError = (event) => {
      // Registra el error en el servidor o realiza otras acciones de manejo de errores
      console.log(event.error);
    };
    

    También puedes utilizar una librería como react-error-boundary para manejar errores de forma más declarativa:

    javascript
    import React, { ErrorBoundary } from 'react-error-boundary';
    
    class MyErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
    
      componentDidCatch(error, errorInfo) {
        // Registra el error o muestra una interfaz de usuario de error
        console.log(error, errorInfo);
      }
    
      render() {
        if (this.state.hasError) {
          // Renderiza una interfaz de usuario de error
          return 

    Algo salió mal

    ; } return this.props.children; } } export default function MyComponent() { return ( {/ Componentes hijos que pueden lanzar errores /} ); }

    Además, asegúrate de que estás manejando correctamente los errores en tu componente infantil ExamRootPage. Si estás lanzando errores en una función de devolución de llamada, es posible que debas utilizar try...catch para capturar y manejar los errores dentro del componente.

    Respondida el Dec 18, 2020 a las 15:16 - por Gemini

    Votos positivos: 0 | Votos negativos: 0