Catching errors thrown from connectedCallback
source link: https://nolanlawson.com/2023/08/25/catching-errors-thrown-from-connectedcallback/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Catching errors thrown from connectedCallback
Posted August 25, 2023 by Nolan Lawson in Web, web components. Leave a Comment
Here’s a deep-in-the-weeds thing about web components that I ran into recently.
Let’s say you have a humble component:
class Hello extends HTMLElement {} customElements.define( 'hello-world' , Hello); |
And let’s say that this component throws an error in its connectedCallback
:
class Hello extends HTMLElement { connectedCallback() { throw new Error( 'haha!' ); } } |
Why would it do that? I dunno, maybe it needs to validate its props or something. Or maybe it’s just having a bad day.
In any case, you might wonder: how could you test this functionality? You might naïvely try a try
/catch
:
const element = document.createElement( 'hello-world' ); try { document.body.appendChild(element); } catch (error) { console.log( 'Caught?' , error); } |
Unfortunately, this doesn’t work:
In the DevTools console, you’ll see:
Uncaught Error: haha! |
Our elusive error is uncaught. So… how can you catch it? In the end, it’s fairly simple:
window.addEventListener( 'error' , event => { console.log( 'Caught!' , event.error); }); document.body.appendChild(element); |
This will actually catch the error:
As it turns out, connectedCallback
errors bubble up directly to the window, rather than locally to where you called appendChild
. (Even though appendChild
is what caused connectedCallback
to fire in the first place. For the spec nerds out there, this is apparently called a custom element callback reaction.)
Our addEventListener
solution works, but it’s a little janky and error-prone. In short:
- You need to remember to call
event.preventDefault()
so that nobody else (like your persnickety test runner) catches the error and fails your tests. - You need to remember to call
removeEventListener
(orAbortSignal
if you’re fancy).
A full-featured utility might look like this:
function catchConnectedError(callback) { let error; const listener = event => { event.preventDefault(); error = event.error; }; window.addEventListener( 'error' , listener); try { callback(); } finally { window.removeEventListener( 'error' , listener); } return error; } |
…which you could use like so:
const error = catchConnectedError(() => { document.body.appendChild(element); }); console.log( 'Caught!' , error); |
If this comes in handy for you, you might add it to your testing library of choice. For instance, here’s a variant I wrote recently for Jest.
Hope this quick tip was helpful, and keep connectin’ and errorin’!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK