Current section: Login 4 exercises

Leveraging Utility Functions for User Data Handling and UI Customization

Loading solution


00:00 The utility we're going to build is in app utils and user.ts. So, this is going to be responsible for the hooks we're going to use to make the user data available everywhere, and we're going to use a couple of utilities for this. So, we're going to import useRouteLoaderData from RemixRunReact,

00:16 and we're also going to import the loader that has the data we're looking for. So, if we look at the root, then here we're loading the user right here, and we're sending it back as part of our JSON. And there are a couple of things we need to keep in mind. The user could potentially be null, so we want to consider that.

00:32 So, coming down here, we're going to export a function called useOptionalUser. And so, this is the user might be on the page, the user might not. We don't really know for sure. And so, we're going to either return the user or return null. So, we're going to say useRouteLoaderData.

00:49 We're going to pass the type of the root loader, so we get this to be type safe, that type inference. And no, it's not data, it's root. This is going to be the ID of the route that has the data that we're looking for. Now, we know that the ID of the route is root,

01:07 because if you recall, it feels like forever ago when we were talking about metafunctions and use matches, the root route ID is going to be root. But if you wanted to look for the loader data for various other routes, then you'd need to provide those IDs as well.

01:26 And if you recall, we use matches to determine what those IDs were. So, you could bring in use matches from Remixer and React, console log it, and then you'll see all the routes that are on the page. Now, something interesting about this is that data that we get back, we're going to return data.user,

01:45 this is going to complain because data could be undefined. So, what can happen here is if we provided some other route ID here, it's possible that that route isn't on the page. Now, for the root route, that is always going to be on the page.

02:03 You can't even render any of these hooks if the root route isn't on the page. But the types for Remix don't know that, and so we have to deal with the fact that data could possibly be undefined, and so we'll add the Elvis operator there. And then because I prefer to return the thing that you were looking for, or null,

02:22 and not bother with undefined, I'm just going to add or null right there. Okay, great. So, let's also export a function called useUser. And this one is going to be for the pages where, like, my loader made sure that the user couldn't even be here if they're not logged in.

02:39 So, like, the useUser should always, always, always return a user. And this will make it easier as far as types are concerned. So, like, in your UI, if you're on the user settings page, which we will have in the future, then you shouldn't have to check for whether the user is defined. The user will always be defined.

02:58 And so what we're going to do here is get the maybe user from useOptionalUser. And then if there's not, not a maybe user, then we're going to throw a helpful error message.

03:11 User not found. If the user is optional, then use useOptionalUser instead. And then we'll return the maybe user. And with that, useUser will always return the user. Otherwise, it throws an error.

03:26 And, of course, you'd only use that on a page that could not possibly show if there is no user. And we will be locking down routes in a future step, or a future exercise, so wait for that. And then we have useOptionalUser where an authenticated or an unauthenticated user can both see the page.

03:44 And we just, like, change some of the things that they see. Okay, so let's go to our route now so we can use these utilities we just built. And right down here, I'm going to swap the data.user for useOptionalUser. And that may seem kind of weird because, like, we literally have the data. Why do we have to go through this hook to get it?

04:02 But I just like the consistency that this offers. That the only way you ever get the user is by using one of those two hooks. So to me, that makes a lot of sense. So now let's go to this page because I want to display different things based on whether I'm logged in

04:18 and looking at myself or if I'm looking at somebody else. So we're going to go to the username route. And, yeah, let's add the ID to the select here. Oh, there it is already there for us.

04:32 And then we'll, based on whether the ID of the optional user matches the ID of the user we're looking at, we'll know whether we are the logged in user.

04:46 So let's grab our optional user or we'll call it, yeah, the logged in user from useOptionalUser. And then whether we're the logged in user depends on whether the IDs match. And so then we can save that.

05:05 And we are, in fact, the logged in user. So we're going to see the logout button. Hooray! Woo! That doesn't work yet. So we've still got to build that. But then if we go to another user's page, we will not see the logout button. So, so far, so good. And that is all using our handy dandy utilities.

05:23 So next we're going to go to our note ID route. And this is going to have some stuff for us to do as well coming down here. So we need to determine whether we are the owner. So here, let's come over to this. And this determines whether we display the trash can and edit buttons.

05:42 So let's get our user, useOptionalUser. And then whether we're the owner depends on whether the user ID matches the data note owner ID. So let's save that. And we should still see that. So that's good. We go to another user's notes. And boom. It's not there.

06:01 Huzzah! Good things are happening, people. Good things are happening. Okay, so you'll notice over here we're not seeing the add new note. And we want to see that because I'm logged in as that user. So let's go to the notes TSX right here. And we're going to do the same sort of thing.

06:18 We're going to grab the user from useOptionalUser. And then we'll determine whether we're the owner based on whether the IDs match. And boom. I see the new note. And then if I come over here and go to this user's notes, I do not see that.

06:35 So we are in a good place with that. And, yeah, actually that's all of them. We've gone through the whole UI. And now as far as the UI is concerned, we're not showing the different UI elements that should not be shown if we're not the user who's logged in.

06:53 And that is awesome. So let's take a quick look and review of our user utility. So here we're going to load the optional user. So load the user optionally. It could potentially be null by grabbing the root loader data.

07:09 And if that exists, then we'll grab the user property off of that. All nice and type safe because of this type of root loader. So if we were to make a change to our root and say, you know what, instead of user, I'm going to say the user. And now, like, everything's blowing up. Look at all that red. Isn't that beautiful?

07:26 I just love the color red when I make a mistake like that. But see here, here's the cool thing. The user. And now, while it all should be fixed, the user. Oh, user. There we go. Hooray, it's all fixed.

07:42 So that's pretty cool that we have TypeScript helping us along the way. So if I want to change this back, now I can go user. And everything's happy and it's awesome. So that's our optional user. And then we just use that in our user and we throw an error. This is kind of like an invariant.

07:59 You could actually probably use our invariant utility instead of throwing this error. That's probably a good idea. And then we return the maybe user, which at this point would not be a maybe user, but would absolutely be a user. And with that, we are able to just take that through all of our UI

08:15 and determine whether to show or hide different things based on that. And we didn't have you actually go through and add that conditional logic to the JSX because that's not the point of this exercise. If you want to do that, feel free to, like, delete stuff and add it back.

08:31 But the point of the exercise was how to get data from one route to other routes. And that is how you do it. So, well done.