We are handling expected errors, but what about unexpected errors?
We'll start by creating a function we'll call
ErrorBoundary to return our error message. What's great about that is we can move
ErrorBoundary to the parent and it will cover all of the children inside of it.
We want it to cover the
posts as well but to do that, we will need to create a parent for it as well using Remix's
Kent C. Dodds: [0:00] Here, we are handling expected errors like a status of 404, but what if we have some unexpected error, like maybe the data is wrong and we're looking for -- I don't know -- post.html() and calling that to get some HTML or something like that?
[0:15] This is a bad example, but maybe this is something that we're trying to do. If we go here, this is not going to work. It's not going to be happy with all about that.
[0:25] How do we handle these errors so that it's not just this generic thing? What we can do is we can export a function called an ErrorBoundary. With that, we're actually going to get a prop called error, and this is going to be our error object.
[0:40] Here, we can simply return div. Maybe, we'll have a className where the text is red, "Oh no, something went wrong!" Maybe you could put a little koala in there, looking all sad and stuff.
[0:56] Then, of course, we can use the error message. Let's do a pre, we'll put the error.message, and then coming over here, "Oh, no, something went wrong! Post.html is not a function."
[1:07] What's cool about this is it's all contextual, but you can actually also do this for an entire part of the app because these errors will bubble. I'm going to remove this from here and we're going to move this to the parent.
[1:19] We can go to our admin, come down there, put it there instead. Now, we're going to get the whole page saying, "Oh, no, something went wrong." That's going to cover any errors that happened with an admin as well as any of its child routes.
[1:32] Now, if we wanted to cover everything that happens within posts, we actually need to make a parent for posts. Right now, we're just using the default. We'll say posts.tsx, and the default is basically export default function PostsRoute and return outlet.
[1:50] This is basically the default when you don't provide a parent route. If we put this ErrorBoundary in there, now we're covering everything under the posts routes.
[2:00] That means if this error happens inside of our index where we, I don't know, throw a new Error('blah') and we try to go to the index route, that's going to be handled because of the bubbling behavior of error boundaries.
[2:15] You can put these anywhere that you like and they will bubble exactly to where you want them to go. Now, if we go to posts/admin and we go to one of these having an error boundary closer to where the error happened, makes the error more contextual.
[2:27] In review, all that you do is in each route that you want to handle unexpected errors, you create an ErrorBoundary component that accepts an error prop and then returns the UI you want to have displayed.
[2:38] Now, one thing you want to watch out for is that error may actually not be an error at all. You could throw a "whatever thing happened" and if we come over here, then our error message doesn't exist because our error is not actually an error.
[2:54] There are two lessons here. First lesson, don't throw random strings. The second lesson, you should probably type this as unknown and handle the unknown case.
[3:05] You could say if error is an instance of Error, then you can return this so that we can have a nice error message. Otherwise, we'll return something that does not have the error message, because we have no idea what's going on.
[3:21] Now, everything's going to work just fine. You can happily throw actual errors to get a better error message. It's just really important to point out that this does not throw any errors because if it does, you're busted. Make sure that you take care that your ErrorBoundary will not throw any errors because it's the last line of defense that you have.
[3:43] The last thing that I should mention about this is that this will indeed catch errors that happen here without issue, which of course we're going to need to remove this error to make that render work, "blah is not defined". It will also catch errors that happen in a useEffect, which is nice, but it's not going to catch errors that happen inside of event handlers.
[4:09] If we say onClick, blah here and click in there, we're going to get errors right here, but that's not going to end up in our ErrorBoundary. The same can be said for async callbacks and that sort of thing. It's just good to know where ErrorBoundaries will help you. I encourage you to think about handling your errors.