El manejo elegante de errores es un aspecto esencial de un software bien diseñado. También es complicado. Este artículo ofrece una descripción general del manejo de errores en aplicaciones React y cómo usar los límites de error de React para manejar errores en el tiempo de renderizado.
Reaccionar tipos de errores
Podemos dividir los errores de la aplicación React en dos tipos, y el manejo de errores en dos aspectos.
Los dos tipos de error de React:
- Errores de JavaScript: estos son errores de JavaScript convencionales que ocurren en la parte del código de un componente.
- Errores de renderizado: son errores generados por el motor de renderizado y que surgen del marcado.
Tenga en cuenta que la naturaleza de la interfaz de usuario de JavaScript dificulta el manejo de errores. Aparte de los errores típicos de tiempo de ejecución, hay errores que surgen del «dibujo» de los componentes de la pantalla. Aquí distinguimos estos dos tipos de errores como «errores de JavaScript» y «errores de procesamiento».
Los errores de JavaScript ocurren en el código y pueden manejarse con bloques try/catch estándar, mientras que los errores de renderizado ocurren en las plantillas de vista y son manejados por los límites de error de React. Podemos pensar en los límites de error como bloques try/catch expresados en el marcado de la plantilla.
Hay dos aspectos del manejo de errores en ambos casos:
- Mostrar información al usuario.
- Proporcionar información al desarrollador.
En general, desea mostrar solo la cantidad mínima de información de error a los usuarios y desea revelar la cantidad máxima de información a los desarrolladores, tanto en el momento del desarrollo como en otros momentos, como las pruebas y la producción. Una buena regla general para el manejo de errores es “fallar suavemente” para los usuarios y “fallar gravemente” para los desarrolladores.
Reaccionar límites de error
El tipo de manejo de errores más distintivo y específico de React es lo que se conoce como límites de error. Esta característica se introdujo en React 16 y le permite definir componentes que actúan como mecanismos de detección de errores para el árbol de componentes debajo de ellos.
Veremos un ejemplo en breve, pero un error de renderizado es aquel que solo se hace evidente cuando el motor React interpreta el marcado. Esto puede suceder en cualquier parte de la jerarquía de componentes que forman parte de una interfaz.
La idea central es crear un widget que represente condicionalmente una vista dependiendo de su estado de error. React proporciona dos métodos de ciclo de vida que un componente puede implementar para determinar si se ha producido un error de representación en su árbol secundario y responder en consecuencia.
Estos dos métodos son componentDidCatch()
y getDerivedStateFromError()
, que es estático. En ambos casos, el objetivo principal es actualizar el estado del componente para que pueda responder a los errores que llegan del motor React.
componenteDidCatch()
El componentDidCatch()
El método es normal y puede actualizar el estado del componente, así como realizar acciones como realizar una llamada de servicio a un servidor de informes de errores. El Listado 1 nos muestra usando este método.
Listado 1. componenteDidCatch
componentDidCatch(error, errorInfo) {
errorService.report(errorInfo);
this.setState({ error: error, errorInfo: errorInfo })
}
En el Listado 1, la función principal garantiza que el estado del componente comprenda que se ha producido un error y transmita la información sobre ese error. Tenga en cuenta que esto componentDidCatch()
tiene acceso al tiempo de ejecución.
getDerivedStateFromError()
Porque getDerivedFromError()
es estático, no tiene acceso al estado del componente. Su único propósito es recibir un objeto de error y luego devolver un objeto que se agregará al estado del componente. Por ejemplo, consulte el Listado 2.
Listado 2. getDerivedStateFromError()
static getDerivedStateFromError(error) {
return { isError: true };
}
El Listado 2 devuelve un objeto con un indicador de error que luego puede ser utilizado por el componente en su representación. Podríamos manejar necesidades más elaboradas construyendo objetos de error más complejos.
Representación basada en error
Ahora, echemos un vistazo al renderizado de nuestro componente de manejo de errores, como se ve en el Listado 3.
Listado 3. Representación de ErrorBoundary
render() {
if (this.state.error && this.state.errorInfo) {
return (
<div>
<p>Caught an Error: {this.state.error.toString()}</p>
<div>
{this.state.errorInfo.componentStack}
</div>
</div>
);
} else {
return this.props.children;
}
}
En el Listado 3 puede ver que la acción predeterminada del componente es renderizar sus hijos. Es decir, es un componente de paso simple. Si se encuentra un estado de error (como en el Listado 1 o el Listado 2), se representa la vista alternativa. Si bien el comportamiento predeterminado es representar la interfaz, un estado de error invoca una ruta alternativa, algo así como un bloque catch.
Usando el componente ErrorBoundary
Ahora has visto los elementos esenciales de un componente de manejo de errores en React. Usar el componente es muy simple, como se muestra en el Listado 4.
Listado 4. Ejemplo del componente ErrorBoundary
<Parent>
<ErrorBoundary>
<Child><Child/>
</ErrorBoundary>
</Parent>
En el Listado 4, cualquier error de representación en <Child>
activará la representación alternativa del manejo de errores <ErrorBoundary>
componente. Puede ver que los componentes del límite de error actúan como una especie de bloque declarativo try/catch en la vista. Cualquier hijo de <Child>
también burbujeará hasta <ErrorBoundary>
a menos que estén atrapados por algún otro límite de error en el camino, también análogo al comportamiento intentar/capturar.
Errores de JavaScript
Los errores de JavaScript se manejan envolviendo el código en bloques try/catch, como en JavaScript estándar. Esto se entiende bien y funciona muy bien, pero hay algunos comentarios que hacer en el contexto de una interfaz de usuario de React.
En primer lugar, es importante tener en cuenta que estos errores no se propagan a los componentes del límite de error. Es posible burbujear errores manualmente a través de las propiedades funcionales normales de React y, de este modo, sería posible vincular el manejo de errores a la representación condicional que se encuentra en sus límites de error.
Errores de red
Los errores de red o del lado del servidor que surgen de llamadas API deben manejarse mediante códigos de error integrados, como se muestra en el Listado 5.
Listado 5. Uso de códigos de error integrados
let response = await fetch(process.env.REACT_APP_API +
'/api/describe?_id='+this.state.projectId, {
headers: { "Authorization": this.props.userData.userData.jwt },
method: 'GET',
});
if (response.ok){
let json = await response.json();
console.info(json);
this.setState({ "project": json});
} else {
console.error("Problem: " + response);
throw new Error(“Problem fetching user info”, response);
}
El objetivo del Listado 5 es utilizar los códigos de estado HTTP estándar para determinar el estado de error de la solicitud de red. (A veces resulta tentador utilizar un campo de “estado” personalizado). En este caso, el error se detecta marcando response.ok
y luego, si hay un error, generamos un nuevo error con throw
. En este caso, esperaríamos que un controlador de capturas se ocupara de la situación.
Finalmente, en relación con los errores de renderizado y JavaScript, recuerde que puede resultar útil registrar errores a través de una API de informe de errores remoto. Esto es manejado por componentes basados en clases que implementan el componentDidCatch
método.
Manejo de errores en acción
Los ejemplos de código de este artículo hacen referencia a CódigoPenque se deriva del ejemplo que se encuentra en el Reaccionar documentos. Este bolígrafo le ofrece cuatro condiciones de error, cada una representada por un color div
con un enlace. El enlace generará un error. La fila inferior muestra errores de JavaScript, la fila superior muestra errores de renderizado. La primera columna no está ajustada con un límite de error, la segunda columna está ajustada.
Esto le da una idea del código y el comportamiento de varios posibles estados de error:
- El cuadro verde arrojará un error de tiempo de renderizado cuando se haga clic y quedará atrapado con un límite, mostrando un mensaje de error.
- El cuadro rojo arrojará un error de renderizado sin límite y, como puede ver, la aplicación fallará y dejará de renderizar.
- Los cuadros azul y morado manejan los errores de la misma manera, ya que el límite del error no captura el error de JavaScript.
Vale la pena examinar estos ejemplos, ya que le brindan todos los elementos funcionales de los límites de error en un paquete de tamaño de bits.
También te puede resultar útil consultar esto. Ejemplo de CodePen de límites de error en React 16.
Conclusión
Puede pensar en los límites de error como bloques declarativos de captura de errores para el marcado de su vista. A partir de React 16, si el renderizado de un componente provoca un error, no se renderizará todo el árbol de componentes. De lo contrario, el error aumentará hasta que se encuentre el primer componente de manejo de errores. Antes de React 16, los errores dejaban el árbol de componentes parcialmente representado.
Los componentes del límite de error deben estar basados en clases, aunque hay planes para agregar soporte de enlace para el ciclo de vida.
Como hemos visto, la idea básica es crear un componente que renderice condicionalmente según el estado de error. Hay dos maneras de lograr esto: la componentDidCatch()
método o el estático getDerivedStateFromError()
método.
Copyright © 2024 IDG Communications, Inc.