Transcript from the "Error Boundaries" Lesson
>> So we are now on to Special Case React Tools here. So the first thing I'm gonna tell you right now is you now are more or less familiar with the Core React Library. You can take what you learned here so far and walk away and write large scale React applications.
[00:00:21] It's one of my favorite parts about React is it's a relatively small cord. It's grown over the years but it's no more than 40, 50 methods slash concepts that you really need to kinda keep in your mind. As opposed to something like at least old school Angular. It was 200 things that you kinda had to keep in mind, and I'm not exaggerating, I think it was 200 methods existed on Angular back in the day.
[00:00:43] I haven't written Angular in a long time, so I don't know what it is now. But it's a relatively small set of tools. Now granted, React does not do for you everything that Angular does for you, and that is a large difference, right? For example, the router is obviously not built in.
[00:00:59] A lot of these libraries have routers and things built into them. React basically says, all that stuff belongs to you, you can choose how you wanna do it, we have no opinion about it. But yeah, that's what I like about React, cuz I can keep the whole library in my head for the most part.
[00:01:19] I don't actually look at the React docs very much because I can remember most of it. As opposed to, especially when I was writing Ember when I worked at LinkedIn, that tab never closed in my browser. I was always in the docs trying to just like, okay, what order do the params come in, how do I do it this way, blah blah blah?
[00:01:41] So we're gonna hop into kinda like three more advanced things that you have to use sometimes. And then everything else, I left in intermediate React, of the harder things that you might need to deal with. So let's hop into error boundaries. Sometimes you have a component that you know might error.
[00:02:11] And a good example, let's say you have a widget that you're writing that a user can put in Markdown, and then you have to go render that into some sort of preview. Because you can't trust what the user is doing, it means you might hit errors, right? In that your Markdown preview window, you might try and render Markdown that's not valid because the user put it there.
[00:02:32] So you have to have some ability to catch an error that comes out of a component. And that's what an error boundary is. An error boundary basically says, containing the blast zone of a component to this component, right? So basically, you're gonna wrap untrusted components with an error boundary so that your errors can't bubble outside of that.
[00:02:57] So fun fact, when you're writing something like nixjs., they will wrap your app in an error boundary so that they can give you in app feedback when you have errors, right? So they use it as kind of a debugging tool. So I'm gonna show you how to write one.
[00:03:14] So let's pop into our editor really quick, and create a new component, and you can call it ErrorBoundary.js or something like that. Now, confession, this is mostly just code directly from the React docs because there's not a lot of variation of that you need here. First of all, these have to be class components.
[00:03:43] Function components are not capable of being error boundaries. So this is one specific case where you absolutely must use a class component. Okay, we're gonna import Link, From React, reacat, that's sounds like a great name for a library, anyway, router-dom. And done, there we go, and we're gonna have class ErrorBoundary, extends Component.
[00:04:25] The state is gonna be equal to hasError: false. Okay, we're gonna make a method called static getDerivedStateFromError. This is a specific name for a function that React can then call. So it's like, what happens when there is an error? What do you want me to give back to your component?
[00:05:00] And you're gonna say, return hasError. Sorry, it's an object, hasError: true. Basically, hey, an error got caught, we're gonna re-render your component. What new state would you like for your component? In this case, I would like the error state to be true. Okay, and then we're gonna give it another method here.
[00:05:51] I'm a big fan of works super well. But you could throw it to something like century as well. So whatever you're using to catch your client side errors, I think New Relic has a service for it as well. This is where you should do that, you would call your API here.
[00:06:05] In my case, my API reporting software is called console.error, Which this is insufficient if you didn't catch my sarcasm. Okay, and then we're gonna render. So we are going to use our error boundary, which is not necessarily the best idea. But what happens if we get an error back from our API?
[00:06:36] Maybe the person made a bad API request or maybe our service is 500ing or we're gonna wrap details basically in an error boundary. Say, if there's some sort of error, then give the user an error message and redirect to the homepage, basically. So, if, this.state.hasError return, Some h2, There was an error.
[00:07:06] No. What are we going to do. Just kidding. We'll put a link in here, Link, Click here to go back to the homepage. Or wait five seconds. And we'll do it for you. Doesn't want me, fine, we'll. You could use the backtick there as well. Or not. I'm not your mom.
[00:07:48] Good error messages are important. It wants me to do it that way. So you could put some sort of a HTML encoded text there. I'm gonna be lazy and just write it out. So you might be wondering what this is, which looks a little strange, right? This is something that Prettier automatically inserted for you.
[00:08:16] It's so that it can line break there for you. And this is just the space that keeps it continuous despite the fact that we're line breaking. But I mean, every time I save, it'll just come back. Which is fine, okay? So this is what happens if there's an error.
[00:08:42] If there's not an error, what do we wanna do? We just wanna render the component normally, right? Yeah, question, Mark.
>> Can the error boundaries only be done in classes?
>> Correct, they cannot be done in function components. Okay, so if there's no error in an error boundary, we just wanna render whatever we were gonna render anyway.
[00:09:10] So what you do is, you say, return this.props.children. Again, remember children is a special named key of whatever came from between it, right? So in this h2, this text here, it's the children of h2, right? So if I make my ErrorBoundary components, it's whatever is gonna be encapsulated by that tag, right?
[00:09:43] Okay, and then that's it, all we got to do at the bottom here is export default. ErrorBoundary. And now, we have a perfectly good error boundary to use with details. All right, so go back to Details, we're gonna import, ErrorBoundary from ErrorBoundary. Good job, VS Code. In our WrappedDetails down here, all we're gonna do is here, we're gonna put our error boundary, and I'll tell you why in just a second.
[00:10:25] Let's do that. So why am I putting it here, inside of WrappedDetails, instead of putting it just directly here inside of my Details component? Wherever the error boundary is, it has to be outside of where you expect the errors to happen, so it has to be a component higher than that.
[00:10:56] So if I'm expecting Details to see errors, right? This is where I would see errors, either from the API calls or whatever. The error boundary has to live above it because if Details hits an error, it's going to throw an error, and it's going to crash all of details, right?
[00:11:13] The entire detail component then crumbles, and that error is gonna start bubbling out of it. So we need to catch it at the component above it. If we put it inside of Details, the error boundary would crumble with the rest of the component, right? Does that make any sense, right?
[00:11:33] So if there's an error here inside of the render function and details was inside of it, it would stop executing the function here inside of details, and then it would throw the error to the parent above it. So if the error boundary was in here, it would just get bypassed and go up to the parent.
[00:11:52] That's why ErrorBoundary has to live above details, which is why we're putting in WrappedDetails. So if I didn't have WrappedDetails already, we just happen to already because we were doing this song and dance, we would have to then make one. Makes sense? Okay, the other place you could have put it, which would have been a little bit weirder, but you totally could have as you could have put it here.
[00:12:19] Wrapping it inside of this element. But I prefer to keep those kind of implementation details more localized to the file here. Okay, so let's show you what that looks like. Okay, so here inside of my render function, it's gonna crash, right? Cuz I throw a new error, and I don't catch it.
[00:12:50] This is great coding, by the way. So now, if I come do Adopt Me. So it did that, which is good. We haven't done the redirect yet, but it should.
>> We didn't put an actual path for the link. We didn't put it to prop.
>> In the error boundary?
[00:13:25] That's probably why. Thank you. So this is what happens when you start making shit up. So to=, and then we're just gonna put that to slash. Yeah, good catch.
>> What causes the redirect?
>> We're gonna do that in a second, good question. So now, we're getting our nice error message that's very descriptive and very not user hostile.
[00:13:59] And then here, they can click here and they go back to the homepage, right? And then if we go into this one, it'll get caught there. And notice that our error reporting service down here, Is reporting the error information. It's printing out the stack trace, And doing all that kind of stuff.
[00:14:21] Okay, so notice, it's not redirecting, but we'll do that here in just a second. So what's nice about this is, now, if we have any sort of API errors or anything like that, this will all just happen to work out. So let's go to the redirect really quick first.
[00:14:42] And we're gonna do that by adding another function here called componentDidUpdate. ComponentDidUpdate, this is another lifecycle method, That every time that the state or props gets updated, this will get invoked, right? So then you can do kind of reactive derivative state kind of things. So in componentDidUpdate, we're gonna say, if this.state.hasError.
[00:15:16] So if this ever gets set to true, then we're just gonna set a timeout, setTimeout. Okay, and then we're going to put an arrow function here so that we don't have to bind anything. This.setState, and we're gonna set redirect to true, and we will invoke this function after 5000 milliseconds.
[00:15:51] Again, this.setState is gonna work because this is a arrow function, and arrow functions are not gonna cause us any problems there. Okay, so we have this redirect state now. So let's track that up here as well in our state redirect: False. We don't have to necessarily do that.
[00:16:15] You could totally do this, and this will be fine. What's nice about doing that, putting this stuff false data even for something that we're not reading off of is that now, TypeScript will have types for this. So it'll know that it has a state called redirect, and that's a Boolean.
[00:16:32] And it's just good to kind of document all the state that you're keeping track of. Okay, so that's there. Then inside of your render function, we have to put a new if statement. If this.state.redirect, return, We're gonna use a navigate component. So Navigate, which I'm gonna get from react-router-dom here, To=/, like that.
[00:17:12] Then we're gonna turn this if into an else if. You can think of navigate as basically a redirect, not basically, actually, navigate as a redirect. So if you render a Navigate component, react-router-dom knows, cool, I'm gonna route that to a different thing. And you can see I imported that at the top.
[00:17:39] Or rather, VS Code imported it at the top for me, so I still have my throw error in there. So if I click Luna, it's gonna say, There was an error. And then hopefully, after five seconds, it should take me back to the homepage, which it does.