diff --git a/src/v2/cookbook/handling-errors-with-error-boundaries.md b/src/v2/cookbook/handling-errors-with-error-boundaries.md new file mode 100644 index 0000000000..c1af2c43cd --- /dev/null +++ b/src/v2/cookbook/handling-errors-with-error-boundaries.md @@ -0,0 +1,286 @@ +--- +title: Handling Errors with Error Boundaries +type: cookbook +order: 11 +--- + +## Base Example + +Handling JavaScript errors in your Vue components is an integral part of building applications. In some cases, a JavaScript error can break a portion of the UI. This behavior can be avoided with the use of an "error boundary". An error boundary is a component that can catch JavaScript errors within child components and render a fallback UI. + +In Vue 2.5.0, the `errorCaptured` hook was introduced which can be used on Vue components. This [hook](https://vuejs.org/v2/api/#errorCaptured) will only capture errors thrown from any descendent components. + +```js +export default { + name: 'ErrorBoundary', + data: () => ({ + error: false + }), + errorCaptured () { + this.error = true + }, + render (h) { + return this.error ? h('p', 'Something went wrong.') : this.slots.default[0] + } +} +``` + +
Check the [guide](https://vuejs.org/v2/guide/render-function.html) if you are unfamiliar with `render` functions in Vue.
+ +A component becomes an error boundary when we utilize the `errorCaptured` hook. Any errors thrown from child components will propagate up to the error boundary. If an error is captured we render the fallback UI of `Something went wrong.
` instead of the slotted content. Otherwise, the slotted content is displayed. + +See the Pen Vue Cookbook: Error Boundary -- Base Example by Dillon (@dchanis) on CodePen.
+ + +In the example above we click the button until it reaches 0 and we use an error boundary to display the error stack. + +## Importance of Error Boundaries + +> "Why would I use an error boundary component?" + +An error boundary allows our application to be safe from UI breaking JavaScript errors. That means if an unexpected JavaScript error occurs we can safely display a fallback UI so our application can run as normal. Furthermore, errors caught by `errorCaptured` propagate up to Vue's global [`errorHandler`](https://vuejs.org/v2/api/#errorHandler) config. This allows us to log our errors to different error logging services to provide insight on why and where errors occurred for our users. + +> "Can I just use a `try/catch`?" + +Yes! However, with Vue's component based architecture it makes more sense to utilize components to handle this logic for us. Components allow us to create reusable bits of code. Instead of writing a `try/catch` everywhere an error may occur, we can allow the error boundary to do it for us. This helps to keep our components free from error handling logic. + +Error boundaries also allow us to be as granular as we want. We could wrap individual components or entire sections of the application. + +## Real-World Example: Contact List + +Using the `v-for` directive is a common part of any application. Let's create a `ContactList` component which will display a list of contacts. Then we will explore how using an error boundary could improve our list when errors arise. + +### Building the ContactList Component + +The `ContactList` component will have an array of contacts which it will need to iterate over using `v-for`. + +```html + +See the Pen Vue Cookbook: Error Boundary -- Contact List by Dillon (@dchanis) on CodePen.
+ + +### Introducing An Error + +Let's update our data source on our `ContactList` component. We will update the phone number to be `null` for one of our contacts. + +`{ id: 2, name: 'Leonardo da Vinci', phone: null }` + +In addition, we do not want to show the hyphens on the phone number. Let's create a [filter](https://vuejs.org/v2/guide/filters.html) on our `Contact` component to remove the hyphens in our contact's phone number. + +```js +// ... +filters: { + withoutHyphens (value) { + return value.replace(/-/g, '') + } +} +``` + +And we will update the template code to utilize our new filter. + +```html +See the Pen Vue Cookbook: Error Boundary -- Contact List with Error by Dillon (@dchanis) on CodePen.
+ + +One of our `Contact` components completely broke! Since our filter is trying to utilize the [`.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) String method on a `null` value we get an error. + +### Introducing the Error Boundary + +Let's see how the error boundary component we defined above can help us. As a reminder, error boundary components wrap existing components that may cause an error. Currently, one of our `Contact` components is throwing an error which is causing nothing to render in its place. We need to update our `ContactList` template to utilize our `ErrorBoundary`. + +```html +See the Pen Vue Cookbook: Error Boundary -- Contact List with Error Boundary by Dillon (@dchanis) on CodePen.
+ + +This is a little better. Thanks to our error boundary we at least get a message that one of the contacts did not load correctly. Let's update our `ErrorBoundary` component to be a little more extendable. + +### Extending our ErrorBoundary component + +Ideally the error boundary component displays a fallback UI that is relevant to the context it is being rendered in. Since the error boundary is just a component we can still have it receive props. We can update the component definition so that it receives a component as a prop to display instead of a static message. + +```js +export default { + name: 'ErrorBoundary', + props: { + fallBack: { + type: Object + } + }, + data: () => ({ + error: false + }), + errorCaptured () { + this.error = true + }, + render (h) { + return this.error ? h(this.fallBack) : this.slots.default[0] + } +} +``` + +We can then create a fallback component to render in case of an error. + +```html + +See the Pen Vue Cookbook: Error Boundary -- Contact List with Error Boundary Pt. 2 by Dillon (@dchanis) on CodePen.
+ + +## When to Avoid This Pattern + +Error boundaries are great but they come with a few caveats. The `errorCaptured` hook will only capture errors in a few situations. + +- render functions +- watch callbacks +- lifecycle hooks +- component event handlers + +DOM event handlers are not included in this. This means if you're `@click` directive or similar directive throws an error the `errorCaptured` hook will not handle it. Instead, refer to the alternative patterns available. + +Another scenario we would want to avoid using an error boundary is when we are wrapping a functional component as explained in this [issue](https://github.com/vuejs/vue/issues/7009). + +Error boundaries should also only be used for displaying production level messages for the end user. Since `errorCaptured` will send all errors to the global `config.errorHandler` we should utilize the `errorHandler` to send all error data to an analytics service rather than in our error boundary. + +## Alternative Patterns + +Since error boundaries act very similarly to [`try/catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) we can still utilize them to handle errors in the cases where error boundaries will not work.