How to Build a React Header With a React Log Out Mutation
A user is now able to successfully sign-in from the Login page. In this lesson, we'll spend some time creating the AppHeader component that will help allow users to navigate around our app.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two with a single-time purchase.
[00:00 - 00:13] We've been able to go through the login flow and essentially log in a user and have the viewer details available on the client app. In this lesson we'll look to notify the user whether they're in the logged in or logged out state.
[00:14 - 00:33] And we'll do this within an app header that will also help the user with navigating between different routes. We'll create an app header component that will be shown in all different pages and will allow the user to navigate around the app, log in and log out.
[00:34 - 00:48] We'll have this app header component be established within a new sections folder. In this app header folder we've also just added an assets that references our application logo.
[00:49 - 01:02] We'll provide this as part of the lesson documentation and source code. The app header components will use the layout header from and design and simply display the logo for now.
[01:03 - 02:29] (Silence) (Silence) Will import and use the link components from React Router to allow the user to click the logo and actually navigate back to the index route depending where they may be in the app. In the parent index file, we'll import the app header component and look to place it at the top of our UI.
[02:30 - 02:58] To have the app header component shown in every route, we'll place it outside of the Router switch statement. To ensure the app header component stays affixed to the top even if we were to scroll down in a page, we'll import and use the affixed components from end design and wrap our app header component with it.
[02:59 - 03:21] By specifying the offset top option to zero, we're stating that we want the child component to stay at the very top of the page. We're now presented with the app header that shows our application logo regardless of what route we're in.
[03:22 - 03:38] We'll now leverage the menu components from end design to help show a menu of links in the app header that the user can use. When the user is signed off, we want to show a link to the host page and a sign -in button to take them to the login page.
[03:39 - 04:08] However, when the user is signed in, instead of the sign-in button, we're interested in showing the avatar of the user that also acts as a drop-down button that allows the viewer to navigate to their profile, or that is to say their user page, or log out of the application . Since this menu item section is to have some decent functionality, we'll abstract this way to a new component we'll call menu items that will set up in the components folder of app header.
[04:09 - 04:48] [silence] At first, we'll aim to have menu items, show the host menu item, and the sign- in button. We'll import and use the button icon and menu components from end design to help us here. We'll destruct the child item and submenu components from end design that we'll use.
[04:49 - 06:00] [silence] [silence] [silence] And we'll use the link component from React Router to have the host item be a link to the slash host route. [silence] And the sign-in button be a link to the slash login route.
[06:01 - 06:35] [silence] We'll now import the menu items component in the app header component and render it as a child of app header. [silence] If we take a look at our UI, we can see the link's being shown, but there's a slight placement issue.
[06:36 - 07:00] If we take a look at our menu items component, we should move the text for host within the link element itself. [silence] When we now click the host link or the sign-in link, we'll be taken to the host page and login page respectfully.
[07:01 - 07:15] We'll now want to conditionally show the viewer profile image if the user is logged in or the sign-in button when the user, that is to say viewer, is logged out. To do so, we'll need access to the viewer state value in our parent app component.
[07:16 - 07:32] In the parent app component, we'll pass the viewer state value as a prop down to the app header component. In the app header component, we'll declare the viewer prop and pass it along down to the menu items child component.
[07:33 - 08:36] We'll import the viewer interface from the lib types file and set the type of the viewer prop to this viewer interface. [silence] [silence] [silence] [silence] With the viewer state value available in the menu items component, we can now conditionally render the drop-down menu or a button element.
[08:37 - 08:54] The drop-down menu will be created with the sub-menu child component and design provides. We'll assign it to a constant element called sub-menu login.
[08:55 - 09:30] This sub-menu login element will have the menu items called profile which will be used as an eventual link to the user page and a log-out item which will be eventually a trigger to help log out to the viewer. We'll conditionally have the sub-menu login element display the sub-menu item if the viewer is logged in.
[09:31 - 09:43] To do so, we can check for a valid property of the viewer object. For example, if the ID of the viewer exists, it most likely means the server was able to return a viewer instance.
[09:44 - 10:10] If the viewer doesn't exist, we'll have the sub-menu login element display the sign-in button and will render this sub-menu login constant element in the return statement of the menu items component. The sub-menu component takes a title prop with which we're able to use to show the avatar of the logged in user.
[10:11 - 10:34] We'll import an avatar component from end design and place it in the title prop and use the avatar property within the viewer object as the source. TypeScript emits a warning and rightfully so since the viewer interface avatar field may be null in certain conditions.
[10:35 - 10:49] However, when the viewer ID exists, we know the viewer avatar field will also exist. So we'll state that both the viewer ID and viewer avatar fields should exist and that is to say not be null to render the sub-menu element.
[10:50 - 10:56] Let's observe what we've done so far. In our UI, we'll log in and go through the consent flow.
[10:57 - 11:11] When being taken back to our app, we'll notice after a very brief period, we're now presented with our avatar image and a drop-down menu. Amazing.
[11:12 - 11:23] As we can see, both items in the drop-down say profile, so we'll go ahead and correct the bottom item to say log out instead. Notice something interesting?
[11:24 - 11:36] When our app refreshed after our change, our login state in the client is gone. That's unfortunate and doesn't really allow the user to maneuver around since they have to avoid refreshing the page.
[11:37 - 11:47] This is going to be the investigation of the next module of the course. For now, we'll re-login and verify that our drop-down log-out action says "Log Out".
[11:48 - 11:54] Great. We'll now wire the log-out mutation with the log-out button in our app header.
[11:55 - 12:08] As a note, if we take a look at the log-out mutation resolver function in the server, it actually doesn't do much right now. It just returns an empty viewer object, something we can just do on the client and set it on the viewer state value.
[12:09 - 12:27] In the next lesson, log-out, or the log-out mutation will help clear out a cookie to prevent the user from having a persistent login state. Though it won't have much of a functionality now, we'll have the log-out item in the drop-down trigger the log-out mutation to set us up for the next module.
[12:28 - 12:59] In the menu item's component, we'll import the log-out mutation document and the auto-generated interface for the data to be returned from the mutation. And we'll also import the use mutation hook.
[13:00 - 13:28] At the top of the menu item's component function, we'll use the use mutation hook and simply return the log-out mutation request function only. In the sub-menu login element, we'll wrap the contents of the log-out item with a div element that when clicked will trigger the component handle log-out function that will be returned from the function.
[13:29 - 13:48] In the log-out function that will subsequently call the log-out mutation function. We're using a div element as to avoid changing the styling of this item, and we 're having a component function call the request since the request is a promise.
[13:49 - 14:10] We'll want to attach a standard function to the click listener that then calls the promise. And in the profile item above, we'll use the link component from React Router to help navigate the viewer to the user page with the respective ID of the viewer.
[14:11 - 14:29] When the log-out mutation is successful, we'll want to notify the user that it was successful and set the viewer client state object in the parent to a value that references a signed-off user. To update the viewer state value, we'll need access to the setViewer function available in the parent app component.
[14:30 - 14:43] In the parent app component, we'll pass the setViewer function as a prop down to app header. In the app header component, we'll declare the prop and pass it along to the menu item's component.
[14:44 - 15:33] In the menu item's components, we'll declare the setViewer function as an expected prop as well. When the log-out mutation is successful, it will return the viewer object type in the offline state, in which all of the values in the object is set to null, and the did request field is set to true.
[15:34 - 16:01] And we'll have this as part of the request data. So we'll use the mutation on completed callback and run the setViewer function to set the viewer state value with the new received viewer objects.
[16:02 - 16:16] We'll also look to display a success notification to tell the user that they were able to sign off successfully. We'll import the display success notification function we have in the libutills file and run it after the setViewer function.
[16:17 - 16:42] If the mutation was to ever fail, we'll want to notify the user this. When the mutation fails in this case, we won't want to render a banner of source or anything in the UI.
[16:43 - 17:00] Instead, we'll want to run the display error message function we have in our libutills file to show an error alert. Since we want to simply run a function, we can do this in the onError callback of the mutation, which is a callback function reactor polar provides that runs when the mutation has failed.
[17:01 - 17:27] Okay, that should help us achieve what we intend to achieve. If we take a look at our app, sign in again and click the logout menu item, we 'll notice that the viewer state has been removed from our clients.
[17:28 - 17:45] Great! Only other change we'll do right now is remove the console log we had in the parent app component that logged in the viewer object. Now, once again, the logout graphQL mutation at this moment doesn't really achieve anything significant.
[17:46 - 18:03] It simply returns an empty viewer object. We could have just created an empty viewer object and set it on the client side of our state viewer value instead. In the next module, we're going to see how the logout mutation will help clear a persistent login state of the user.