diff --git a/doc/readme.md b/doc/readme.md index 6dccb40b..c0e7db0c 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -25,6 +25,7 @@ provided by Owl. - [Environment](reference/environment.md) - [Event Bus](reference/event_bus.md) - [Event Handling](reference/event_handling.md) +- [Error Handling](reference/error_handling.md) - [Hooks](reference/hooks.md) - [Miscellaneous Components](reference/misc.md) - [Observer](reference/observer.md) diff --git a/doc/reference/component.md b/doc/reference/component.md index 72662393..b03712f1 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -15,7 +15,6 @@ - [Form Input Bindings](#form-input-bindings) - [References](#references) - [Dynamic sub components](#dynamic-sub-components) - - [Error Handling](#error-handling) - [Functional Components](#functional-components) - [SVG components](#svg-components) @@ -306,16 +305,16 @@ A solid and robust component system needs useful hooks/methods to help developers write components. Here is a complete description of the lifecycle of a owl component: -| Method | Description | -| ------------------------------------------------ | ------------------------------------------------------------ | -| **[constructor](#constructorparent-props)** | constructor | -| **[willStart](#willstart)** | async, before first rendering | -| **[mounted](#mounted)** | just after component is rendered and added to the DOM | -| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update | -| **[willPatch](#willpatch)** | just before the DOM is patched | -| **[patched](#patchedsnapshot)** | just after the DOM is patched | -| **[willUnmount](#willunmount)** | just before removing component from DOM | -| **[catchError](#catcherrorerror)** | catch errors (see [error handling section](#error-handling)) | +| Method | Description | +| ------------------------------------------------ | ----------------------------------------------------------- | +| **[constructor](#constructorparent-props)** | constructor | +| **[willStart](#willstart)** | async, before first rendering | +| **[mounted](#mounted)** | just after component is rendered and added to the DOM | +| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update | +| **[willPatch](#willpatch)** | just before the DOM is patched | +| **[patched](#patchedsnapshot)** | just after the DOM is patched | +| **[willUnmount](#willunmount)** | just before removing component from DOM | +| **[catchError](#catcherrorerror)** | catch errors (see [error handling page](error_handling.md)) | Notes: @@ -450,8 +449,8 @@ This is the opposite method of `mounted`. #### `catchError(error)` The `catchError` method is useful when we need to intercept and properly react -to (rendering) errors that occur in some sub components. See the section on -[error handling](#error-handling). +to (rendering) errors that occur in some sub components. See the page on +[error handling](error_handling.md). ### Root Component @@ -744,66 +743,6 @@ component class. Note that the `t-component` directive can only be used on `` nodes. -### Error Handling - -By default, whenever an error occurs in the rendering of an Owl application, we -destroy the whole application. Otherwise, we cannot offer any guarantee on the -state of the resulting component tree. It might be hopelessly corrupted, but -without any user-visible state. - -Clearly, it sometimes is a little bit extreme to destroy the application. This -is why we have a builtin mechanism to handle rendering errors (and errors coming -from lifecycle hooks): the `catchError` hook. - -Whenever the `catchError` lifecycle hook is implemented, all errors coming from -sub components rendering and/or lifecycle method calls will be caught and given -to the `catchError` method. This allows us to properly handle the error, and to -not break the application. - -For example, here is how we could implement an `ErrorBoundary` component: - -```xml -
- - Error handled - - - - -
-``` - -```js -class ErrorBoundary extends Component { - state = useState({ error: false }); - - catchError() { - this.state.error = true; - } -} -``` - -Using the `ErrorBoundary` is then extremely simple: - -```xml - -``` - -Note that we need to be careful here: the fallback UI should not throw any -error, otherwise we risk going into an infinite loop (also, see the page on -[slots](slots.md) for more information on the `t-slot` directive). - -Also, it may be useful to know that whenever an error is caught, it is then -broadcasted to the application by an event on the `qweb` instance. It may be -useful, for example, to log the error somewhere. - -```js -env.qweb.on("error", null, function(error) { - // do something - // react to the error -}); -``` - ### Functional Components Owl does not exactly have functional components. However, there is an extremely diff --git a/doc/reference/error_handling.md b/doc/reference/error_handling.md new file mode 100644 index 00000000..5c64b577 --- /dev/null +++ b/doc/reference/error_handling.md @@ -0,0 +1,81 @@ +# 🦉 Error Handling 🦉 + +## Content + +- [Overview](#overview) +- [Example](#example) +- [Reference](#reference) + +## Overview + +By default, whenever an error occurs in the rendering of an Owl application, we +destroy the whole application. Otherwise, we cannot offer any guarantee on the +state of the resulting component tree. It might be hopelessly corrupted, but +without any user-visible state. + +Clearly, it sometimes is a little bit extreme to destroy the application. This +is why we have a builtin mechanism to handle rendering errors (and errors coming +from lifecycle hooks): the `catchError` hook. + +## Example + +For example, here is how we could implement an `ErrorBoundary` component: + +```xml +
+ + Error handled + + + + +
+``` + +```js +class ErrorBoundary extends Component { + state = useState({ error: false }); + + catchError() { + this.state.error = true; + } +} +``` + +Using the `ErrorBoundary` is then extremely simple: + +```xml + +``` + +Note that we need to be careful here: the fallback UI should not throw any +error, otherwise we risk going into an infinite loop (also, see the page on +[slots](slots.md) for more information on the `t-slot` directive). + +## Reference + +Whenever the `catchError` lifecycle hook is implemented, all errors coming from +sub components rendering and/or lifecycle method calls will be caught and given +to the `catchError` method. This allows us to properly handle the error, and to +not break the application. + +There are important things to know: + +- If an error that occured in the internal rendering cycle is not caught, then + Owl will destroy the full application. This is done on purpose, because Owl + cannot guarantee that the state is not corrupted from this point on. + +- errors coming from event handlers are NOT managed by `catchError` or any other + owl mechanism. This is up to the application developer to properly recover + from an error + +Also, it may be useful to know that whenever an error is caught, it is then +broadcasted to the application by an event on the `qweb` instance. It may be +useful, for example, to log the error somewhere. + +```js +env.qweb.on("error", null, function(error) { + // do something + // react to the error +}); +```