#javascript #reactjs
#javascript #reactjs
Вопрос:
Я обнаружил, что пытаюсь создать обработчик ошибок с ErrorBoundary, однако через пару дней и много терпения мне удалось зафиксировать ошибку и сохранить ее в базе данных, вызвав API в серверной части.
ПРИМЕЧАНИЕ: если у вас есть лучшее решение, пожалуйста, прокомментируйте этот пост.
Первое: создайте свой компонент ErrorBoundary, как указано в документации ReactJS, за исключением пары изменений, которые добавляют
Как только вы вводите componentDidCatch, ошибка Boundary отправляется непосредственно в рендеринг, он не проходит через другой жизненный цикл React, затем мы создали другой промежуточный компонент, в котором можно вызывать API и иметь возможность спокойно использовать жизненные циклы, но мы добавили метод обратного вызова, чтобы уведомить ErrorBoundary о том, что ошибка уже была уведомлена, и перенаправить представление home или любое другое ваше основное представление.
первое: мой компонент 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(/(rn|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 amp;amp; 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" amp;amp; 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" amp;amp; 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, и мы можем использовать метод фиксации этих ошибок в нашем ErrorBoundary, чтобы у нас была централизованная точка ошибки, поэтому мы добавляем global.onunhandlerrejectionв жизненный цикл componentDidMount таким образом, если обработчик ошибок BlueBird обнаружит ошибку в каком-либо обещании, он примет ее в нашем ErrorBoundary.
Комментарии:
1. Вы просите отзыв?
2. да, а также, если кто-то не знает, как использовать componentDidCatch и отлавливать ошибки асинхронного кода, я покажу вам, как я мог бы это решить
3. Добро пожаловать в Stack Overflow. Если вы разделите это на вопрос и ответ, вы можете «самостоятельно ответить» на него и получить оценку. Прямо сейчас, как запрос на проверку кода, это, вероятно, будет расценено как не по теме. В сети Stack Exchange есть отдельный сайт для обзоров кода, codereview.stackexchange.com но вы захотите прочитать их обзор, прежде чем использовать этот сайт.