Authentication state is one of the things that can really twist up our application's components with a bunch of if statements for handling that state. But there's a simple trick you can apply to reduce the complexity of your components.
This is based on my blog post: Authentication in React Applications
Instructor: [0:00] Here we have a simple example of an application, where you refresh here, reload the user, and then, when the user's loaded, we show the application. Pretty common scenario here. We have an auth provider that's using authContext. While the user's loading, we render loading. If there's an error, we show the error. Otherwise, we'll render the children.
[0:19] Then, down here at the very bottom, we have our app which renders that auth provider. Some JSX here for the app, and then our home screen, which renders our header content and footer. The important part here is, the header content and footer are littered with this useAuthState hook that will give us the user.
[0:39] Then we have these ternaries all over the place, determining what we should show based on whether the user is logged in. Here we have Elmo logged in. If I comment this in, it's going to return null, so nobody is logged in here. Now we're going to see, "Please log in. You must log in to read the message."
[0:59] Then we don't have anything for the footer, because we just decided we don't need to render anything for the footer.
[1:04] This is a really common situation that you'll see in applications, where you have to do a lot of checking whether the user is currently logged in, to determine what you're going to show the user. There's a little trick that you can use to make this thing a lot easier for tons of the components in your application. That is, move this ternary to one place.
[1:27] What I'm going to do here is I'm going to say, "Hey, this content is great, but what if I just had two different versions of the same component?" I'm going to have a function and we'll have an unauthenticated content. Then, I'm going to move this down to here, we'll return that there. We'll get rid of the ternary and get rid of all that.
[1:54] This content is just for the authenticated user. I'm going to go ahead and count this out, so we'll just see the authenticated user stuff. Great. We're seeing that content. It's working just fine. We'll have to deal with that unauthenticated content.
[2:08] Already we can see that this content itself is a lot simpler because it doesn't have to worry about whether the user is logged in or not. It just assumes that the user is logged in. The same with the unauthenticated content component, it just assumes that the user is not logged in.
[2:22] Then here, we have a header component that's doing a similar ternary, so I'm going to make an authenticated header. We're going to grab that, we'll return that, and then get rid of the ternary here.
[2:36] The header can just assume that the user is authenticated, this one assumes that they're not. We don't need an authenticated footer, because we're not rendering anything there anyway. Let's just get rid of this and we can get rid of that.
[2:50] We can just assume that the user is logged in. All this can assume that the user is not logged in, but we need to know when to render these different components.
[2:58] What I'm going to do is, I'm going to make a new function for the authenticatedApp. The authenticatedApp is going to return what our home was returning before.
[3:08] Then, we'll have another function here for an unauthenticatedApp. This one's going to return the same stuff conceptually. Instead of the header, we're going to render the unauthenticated header and the unauthenticated content, and we're not going to render a footer.
[3:24] Then, the ternary does need to happen somewhere. We do need to say, "Show this if the user is logged in, and show this if they're not." That's what home is going to do.
[3:32] Now, we're going to get our user from useAuthState. We'll return, if there's a user, the authenticatedApp. Otherwise, we'll return the unauthenticatedApp.
[3:48] With that, we're getting the exact same experience that we had before, where we can see, "Hello, Elmo. I'm so happy to have you here, Elmo. This is an awesome app." We're getting all that from our authenticatedApp. If we're unauthenticated, so if there is no user, then we're going to get the "Please log in, you must log in to read the message."
[4:06] At the end of the day, we can see that our components themselves are way simpler. There's no logic in here at all, we've just moved all that logic to one centralized location.
[4:17] If there happens to be some shared content here like we said, we want the footer to appear in both, then we can render that in both, and that's no problem at all.
[4:26] In some situations, there may be some content within this footer that does need to change. For example, if we were to say, we have a user use useAuthState. We want to show the user's username if the user exists. Then here we can say if there's a user, we'll say user.username. Otherwise, we'll say null. That works out just fine.
[4:47] You can go ahead and share components between your unauthenticated and the authenticated side of your application. A lot of the components can be totally separate and have nothing to do with one another, making it a lot easier to do things like code splitting and organizing the file system.
[5:02] Ultimately, it leads to your application having fewer bugs and edge cases, because you didn't consider whether the user is authenticated or not. You don't need to when you take the approach of separating your authenticated and your unauthenticated components in this way.
This may work fine in this example, but in the real world you have component like a header or navigation that contain components that changed based on log in status. (e.g. Login/Logoff, display Profile, etc...). This seems mostly academic to me.