This component provides a simple and reusable wrapper that you can use to wrap around your components. Any rendering errors in your components hierarchy can then be gracefully handled.
This module is distributed via [npm][npm] which is bundled with [node][node] and
should be installed as one of your project's dependencies
:
$ npm i vu-error-boundary
# or
$ yarn add vu-error-boundary
The simplest way to use <ErrorBoundary>
is to wrap it around any component
that may throw an error. This will handle errors thrown by that component and
its descendants too.
<script>
import {ErrorBoundary} from 'vu-error-boundary'
</script>
<template>
<ErrorBoundary @reset="reset">
<ComponentThatMayError />
<template #fallback="{ resetErrorBoundary, error }">
<div role="alert">
<p>Something went wrong:</p>
<pre>{{ error.message }}</pre>
<button @click="resetErrorBoundary">Try again</button>
</div>
</template>
</ErrorBoundary>
</template>
You can vue to errors (e.g. for logging) by providing an @error
callback:
<script>
import {ErrorBoundary} from 'vu-error-boundary'
export default {
setup() {
const handleError = useErrorHandler()
const myErrorHandler = (err: unknown, instance: ComponentPublicInstance | null, info: string) => {
// Do something with the error
// E.g. log to an error logging client here
}
return {
myErrorHandler
}
},
}
</script>
<template>
<ErrorBoundary @error="myErrorHandler">
<ComponentThatMayError />
</template>
This is what you want rendered when everything's working fine. If there's an
error that Vue can handle within the children of the ErrorBoundary
, the
ErrorBoundary
will catch that and allow you to handle it gracefully.
This is a component you want rendered in the event of an error. As props it will
be passed the error
and resetErrorBoundary
(which will reset the error
boundary's state when called, useful for a "try again" button when used in
combination with the onReset
prop).
This is required if no fallback
or fallbackRender
prop is provided.
This will be called when there's been an error that the ErrorBoundary
has
handled. It will be called with two arguments: error
, info
.
This will be called immediately before the ErrorBoundary
resets it's internal
state (which will result in rendering the children
again). You should use this
to ensure that re-rendering the children will not result in a repeat of the same
error happening again.
onReset
will be called with whatever resetErrorBoundary
is called with.
Important: onReset
will not be called when reset happens from a change
in resetKeys
. Use onResetKeysChange
for that.
Vue's onErrorCaptured
feature is limited in that the boundaries can only
handle errors thrown during Vue's lifecycles. To quote
Error boundaries do not catch errors for:
- Event handlers
- Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
- Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
This means you have to handle those errors yourself, but you probably would like
to reuse the error boundaries you worked hard on creating for those kinds of
errors as well. This is what useErrorHandler
is for.
There are two ways to use useErrorHandler
:
const handleError = useErrorHandler()
: callhandleError(theError)
useErrorHandler(error)
: useful if you are managing the error state yourself or get it from another hook.
Here's an example:
<script>
export default {
setup() {
const handleError = useErrorHandler()
function handleSubmit(event) {
event.preventDefault()
const name = event.target.elements.name.value
fetchGreeting(name).then(
newGreeting => setGreeting(newGreeting),
handleError,
)
}
return {
handleSubmit
}
},
}
</script>
<template>
<div v-if="greeting">{{greeting}}</div>
<form v-else @submit="handleSubmit">
<label>Name</label>
<input id="name" />
<button type="submit">get a greeting</button>
</form>
</template>
Note, in case it's not clear what's happening here, you could also write
handleSubmit
like this:
function handleSubmit(event) {
event.preventDefault()
const name = event.target.elements.name.value
fetchGreeting(name).then(
newGreeting => setGreeting(newGreeting),
error => handleError(error),
)
}
Alternatively, let's say you're using a hook that gives you the error:
<script>
export default {
setup() {
const name = ref('')
const {greeting, error} = useGreeting(name)
useErrorHandler(error)
function handleSubmit(event) {
event.preventDefault()
name.value = event.target.elements.name.value
}
return {
handleSubmit
}
},
}
</script>
<template>
<div v-if="greeting">{{greeting}}</div>
<form v-else @submit="handleSubmit">
<label>Name</label>
<input id="name" />
<button type="submit">get a greeting</button>
</form>
</template>
In this case, if the error
is ever set to a truthy value, then it will be
propagated to the nearest error boundary.
In either case, you could handle those errors like this:
<template>
<ErrorBoundary>
<Greeting />
<template #fallback="{ resetErrorBoundary, error }">
...
</ErrorBoundary>
</template>
And now that'll handle your runtime errors as well as the async errors in the
fetchGreeting
or useGreeting
code.
Looking to contribute? Look for the [Good First Issue][good-first-issue] label.
Please file an issue for bugs, missing documentation, or unexpected behavior.
[See Bugs][bugs]
Please file an issue to suggest new features. Vote on feature requests by adding a π. This helps maintainers prioritize what to work on.
[See Feature Requests][requests]
MIT