Current section: Search Engine Optimization 6 exercises

Implementing Dynamic Meta Data for Routes in a TypeScript Application


00:00 Let's start with the note ID route, so note ID. And we'll come down here, and we've got a meta function that Kelly, our co-worker, built for us. And we also are using this matches thing. So let's go ahead and console.log matches and see what that thing is at all. So look at our console.

00:19 Here we have that log, and there are three parts of this matches array. So there are three routes that are currently matching on the page. We have the route that's the root route that has our data for our username down at the bottom and the environment variables, all that stuff. We have our meta export from that.

00:38 We have the params. We have the path name. And we have this very important ID, which we're definitely going to be using. Then we also have this next route. This is our parent route. And in fact, our parent route has the data that we need. So here we're trying to display the display name of the user that we're looking at,

00:58 the owner of the note. And so we need to get that from data.owner. And that owner has a name right there. So that's what we're trying to access here. And then, of course, we do have one last match here. And that's the route that we're currently on. That has the note data and everything. But we've already got access to that

01:18 thanks to our type of loader and our data right here. So the match we want is the index 1, or the second match here. But we're going to find it using its ID. And part of the reason that we're doing that is, one, I think it's more clear.

01:35 And then, two, TypeScript actually can we can inform TypeScript a little bit better by doing things this way. So the first thing that I'm going to do is we're going to add another parameter to this meta function

01:50 generic, which is to say it's an object that is the data value or the data types from our matches. So the key is the ID. And then the value is the type of the loader for that particular match.

02:09 This type of loader is almost there. This is the type of our own loader. We actually need the type of our parent loader. So I'm going to import the type loader as note loader. And we're doing that as note loader so we don't conflict with our own loader

02:27 from the notes TSX file. So this is our parent route. We're importing the type of the note loader from our parent route. And then we can come down here and say the data that is available from this route with this ID has this type. That's what is going on.

02:44 Or its loader is this type. And so from that, then, we can follow Marty the Moneybag's suggestion of finding the route there. So we can find that as our note match. And right here, if we console log that, we can take a look at that log right there. There it is.

03:07 There's our data. And on top of that, because we've informed TypeScript of our convention here and provided that to the generic, it is able to tell us what the type is. And so this is actually really cool. And we'll explore that here in just a second. But let's finish this feature. And then we can talk about that.

03:24 So for getting the display name, we can say note match.data. And you'll notice VS Code added the Elvis operator there. And that's because it's possible that note match is not defined. Possibly, we have the ID wrong. TypeScript will kind of help us make sure we don't.

03:44 But it is possible that could happen. And then secondly, it's possible that the loader failed to run. But our meta will still run. And so we're going to cover ourselves for that. So if the note match exists, we'll access its data property. We'll go to owner and then name. And we'll say, if that doesn't exist,

04:04 then we'll fall back to the params name. So now with that, I can come over here. And we get Cody with a capital K. So that's Cody's actual name. So everything is working. But I want to talk a little bit about why it's so awesome that we can provide this type and why I feel comfortable doing this.

04:23 If we were writing this just in JavaScript, I would be a little bit uneasy doing this because there's a pretty severe level of indirection here. I mean, we are importing the loader and stuff. But the loader, and actually, that's all TypeScript. So we wouldn't be importing the loader if it was just JavaScript.

04:41 So this is severe level of indirection going on here that if I'm working in the notes route right here, and I'm making changes to this, and I say, you know what? The owner doesn't need to have all this stuff. Let's have the owner have a name. And that'll be our owner.name.

05:00 And an owner or the username, owner.username. And that's great. So what if we decided, you know what? In this meta, we're going to say, we're going to put their email instead. That probably wouldn't be a good idea from a product decision standpoint. But yeah, definitely could be a thing you could do.

05:20 So before, that works just fine. No problem, because we've got all that data. But then if I'm in here and I'm like, you know what? We don't need all that extra stuff. Let's just filter down to the stuff that we're actually using. Now, because we have all this type stuff put together, I get a type warning right here. Without JavaScript, that would not be possible.

05:39 Or without TypeScript, that would not be possible. If we were just using JavaScript, I would feel way uneasy about doing this. So TypeScript, it's great. I highly recommend it. OK, so with that, let's go ahead and go to the index route. And we'll do the same thing here. It'll be a little bit different.

05:58 So let's go Notes, Index. And remember, we don't have a loader. So our meta function isn't even using the generic, because we're not going to access the data, because there is no data. But we want to have some data so that we can have the display name include the user's actual name.

06:17 And also, we need to determine the number of notes that they've got. So right now, we're just saying Cody's zero notes. But Cody clearly has many more notes. So the same thing applies here. The difference is, because we don't have a loader, we're just going to pass null as the first argument to this generic. The second thing we're going to do

06:35 is similar to what we did before, type of note loader. And I'm going to go grab that import and stick it right here. So we get that note loader here as well. Now, of course, that's not type of note loader. It's actually an object that has the key being the ID of the route, and then the type being the value. OK, great.

06:55 So now, we can follow Marty the Moneybag's suggestion again, get our notes match. And now, that's all going to be nice and type safe and everything else that I had talked about before. And so we can use that to determine the user's name. So we can say notes match.data, again,

07:14 with the Elvis operator here so that we don't have to worry about the notes match not being defined. Then we'd say owner.name. Otherwise, fall back to the username. And then same thing here. It will be notes match.data.notes.length,

07:33 or fall back to 0. And that takes care of that. So now, it's Cody's Notes with a capital K. And check out Cody's 12 notes on Epic Notes. So just a quick review, the primary learning outcome here is that the second argument of the generic meta function

07:52 here is the data types or the type of the loaders of all of the matches that are relevant to what you are doing. And so you provide the ID of the match, and then its loader type. And then your matches is all of the routes that are matched, whether or not those are included in here.

08:11 At runtime, this is just going to be all the matches. You can find things or filter them by their ID. And then all of this will be type safe, which is pretty stellar, I must say. So with that established, we have accomplished the goal. And now our users will have a much better metadata experience.

08:30 And they'll be more likely to share their stuff on social media, which is good for our app. So I hope that was interesting and fun for you. And yeah, well done.