Finalizing the Logout Functionality with a Higher Order Component
It's time to finish out the log out functionality and like I mentioned earlier, we're going to use this as a tool for learning how high order components work.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

We're not gonna talk about high order components, they're also called HOCs until later on because if I were to tell you about 'em right now, your very first question might be why in the world would we ever need this 'cause they look kind of weird and so, I don't want to confuse you right away, so instead I wanna show you why they're necessary first, so we're gonna build out the majority of our implementation and then you're gonna see why they're needed and also how to use them.

So, let's switch into Visual Studio Code and open up the navigation-container.js. Now, the very first thing that we're gonna have to do is import Axios because we're going to have to communicate to that log out endpoint, so I'll say import axios from axios and then inside of the NavigationComponent function, we're gonna create a new function and we're going to call it handleSignOut.

So come down above the return and below the dynamicLink function and right here let's create it, so I'm gonna say const handleSignOut and we'll make it an arrow function that doesn't take in any arguments and now let's call axios, so I'm gonna say axios.delete and the API endpoint we're gonna hit is gonna be https://api.devcamp.space and then it is /logout I believe.

Now with that, we also have to send withCredentials, so give it a comma and all of these, this IntelliSense is very annoying, I am gonna research how to just turn that off entirely. So, after logout, give it a comma and then an object and inside of here type withCredentials and you want this to be true and save.

navigation-container.js

const handleSignOut = () => {
  axios.delete("https://api.devcamp.space/logout", {withCredentials: true})
};

So, this is going to be the first part of the implementation, this is going to call axios and this is a verb that we've not hit before called the delete verb. So far we've used get, remember, get is the verb we use to pull data down or to run queries. Post is what we use when we want to create something. Delete is kind of self-explanatory, it's when you want to delete something.

So we're going to call this API endpoint, we're going to say I want you to delete something. In this case, it's the session and we're authenticating ourselves as normal withCredentials true, so now that we have that, remember that is going to make the call but then we need to receive the promise or we will receive a promise and we need to tell the system what we want to do then, so we're gonna do a couple of things.

I'm gonna say .then and I'm just gonna keep going on this line and then Prettier will move it down and format it accordingly. So, for then I want to take the response and then pass it an arrow function and then inside of here, this is where we're going to say this is what I want you to do when you are done, so when we get the confirmation back that we're logged out, I want you to perform these tasks.

The first one is going to be and I'm gonna put this in a conditional, so I'm gonna say if response.status 'cause this is what we're gonna get back is a status, so I'm gonna say if that response status is 200 which means that it worked, then I want you to say props.history.push and then I want you to go back to the home page from no matter where you're at, and I'm going to give you a little bit of a spoiler alert. This is not going to work and this is why we're going to need the high order component.

Now, if you're wondering, on line 18 how do I know that this is the conditional to check for? There are a couple of reasons. One, it's because I built the API it connects to and I know I built it so it returns a 200 whenever this runs successfully. If an API is built right, when you call a delete endpoint, it should always send you back a 200 status if it was deleted correctly, so that is what should occur.

Now, if you're working with a different type of API, every API can be a little bit different, so you have to look at the documentation and then also play around with it and see what it turns back to you but for right now just know this is what the API returns, any API you work with in your entire life should have documentation that should tell you what gets returned when you make a certain call. So, that's how I know that and that's how I know to check for the response.status.

Now I want to push to the Home page which we already know is not going to work yet but we're gonna understand why here in a minute, and then the other thing we wanna do is say props.handle and let me just copy this to make sure I get the case properly called, so handleSuccessfulLogout and then we're just going to call that function. So, remember, this is the handler that we pushed in, in the last guide where we are gonna update the state in the app component.

So, that is what we wanna happen and then at the very end let's just say return response and data, it doesn't really matter, we're not using this at all. The reason why I do this is because it's considered a poor practice to not return something when you call then on a promise, so I'm doing that just to kind of follow conventions. And then from there, let's also add a catch, so I'm gonna say catch just in case there are any errors. We're gonna catch those errors and I'll console.log Error signing out and then let's call the error, and we're nice and formatted now.

navigation-container.js

const handleSignOut = () => {
  axios
    .delete("https://api.devcamp.space/logout", { withCredentials: true })
    .then(response => {
      if (response.status === 200) {
        props.history.push("/");
        props.handleSuccessfulLogout();
      }
      return response.data;
    })
    .catch(error => {
      console.log("Error signing out", error);
    });
};

Now that we have this handleSignOut, we can actually call it from within our JSX code, so scroll down, all the way down to the bottom where you have your name here on the right-hand side div and we're gonna add a conditional because we don't want this sign out link to be shown unless a user is signed in, it wouldn't make any sense for a guest user to see a sign-out link.

So we're gonna put this here only if you're signed in which we've done this a few times, so you probably have an idea of what needs to happen, we need to create a turnery operator to check to see if we're logged in or not, so I'm gonna start with curly brackets, say props.loggedInStatus which we have access to in this component. We're gonna check to see if we're LOGGED_IN and then if we are, I'm going to wanna return a link, so for this link let's say I want it to be just a regular a tag and we're gonna have an onClick listener and this onClick listener is going to call handleSignOut.

Now, if you're curious on why I'm not having to say something like this.handleSignOut, it's because we're in a functional component. You only need to use the this keyword if you're in a class component, if you're in a functional component, you can just call the name of the function. It's part of the reason why a lot of developers try to use functional components as much as possible.

Now that we have this handleSignOut call, now we can close the first part of the a tag and I'll just say Sign Out, that's just gonna be the text, you could have it say whatever you want and later on, we're gonna make this an icon. We're gonna use font Awesome and we're gonna use a custom icon for that and so, that is the first part, so we're saying if you're signed in, I want to show this SignOut link and if not, just show null. So, we have the first part of our implementation done.

navigation-container.js

<div className="right-side">
  JORDAN HUDGENS  
  {props.loggedInStatus === "LOGGED_IN" ? (
    <a onClick={handleSignOut}>Sign Out</a>
  ) : null}
</div>

Now, this is going to not work like I mentioned because we're gonna have to use a high order component but as you can see, we have our link here, and we have it pointed to a handleSignOut function that's gonna call delete on the logout endpoint which is gonna delete the session and then we want it to redirect us to the Home page and then also update the state, so let's test all of this out and then take it from there.

So, we're starting off in a state where we're not logged in as we can see, so we shouldn't see anything here on the right-hand side and now if I go to /auth, let's log in and now if we hit Login, we can see our Blog link, we are logged in for the app state and now we see that we have this Sign Out link.

large

Let me open up the JavaScript console, don't worry about this warning right here, we're gonna take care of that in a future section, it just has to deal with the links that we're calling here and I'll show you how to fix that. But now if you click Sign Out, you can see it says error signing out TypeError: Cannot read property push of undefined.

large

This is where it gets weird and the very first time that I ever saw this, I was pretty confused because the documentation is not great on this and this error doesn't make a lot of sense. So, let's try to dissect it, let's pretend that we don't know what the solution is. So, I cannot read property push of undefined.

The first time that I will see an error like this, something that maybe I've never seen before, the very first thing you should do is look at where the error's occurring, so if you click this link, it shows you exactly where it's happening. It's in this line where it says props.history.push.

Now, what the error is telling us if you read into it, it says cannot read property push of undefined. That means that push is being called on something undefined, so if you switch over here, that means that history is undefined which means we do not have access to the browser history, the same history that we had access to, let's see, where is it? I think it was in our login or yeah, it was in either one of our login, or no, it was the auth component if I remember correctly.

Yes. So, right here, you notice how we have access to the history inside of props here. It's because in this case, we're actually in a route. So, auth, if you remember, is a route, it's a dedicated route that we list inside of our route definitions.

So if you come here, you can see we have our Auth component. Because of this, it means that you actually have kind of secretly, this is something that's not very evident until you really dig into it, you secretly have access to some things that you wouldn't even realize like the history, so in this case, you are able to do things like redirect and push to the history.

But if you're using a kind of a partial component here, this is one that doesn't have a dedicated route, then we're not able to use this and so, this could be very confusing if you thought, hey, I just wanna redirect someone. It should be easy. Well, there thankfully is a way around it and it is with high order components. If you come here, I have a link to the documentation and I highly recommend for you to read this because this is an advanced technique.

I've not seen very many introductory React courses that even mention these, much less show how to use them because they can be a little bit confusing. So, let's read the definition really quick and then we'll talk about it. So, a higher-order component, or HOC, is an advanced technique in React for reusing component logic. HOCs are not part of the React API and this is something I want to be very clear. They are a pattern that emerges from React's compositional nature.

So, they give an example one right here where they say const EnhanceComponent equals higherOrder Component and then you pass in a WrappedComponent. So, essentially what a higher-order component does is it allows you to wrap up certain functionality and then add that to a function or a class or something like that, so that's where it can be a little bit confusing if you've never seen it before but some of the keywords here are React's compositional nature.

What this means is React is a very big fan of composing features and composing functionality and if you've never heard that term before, it kind of is just what it sounds like. It means that you're composing, you're putting things together, just so like the way a composer for an orchestra takes in a violinist and a piano and a guitar and all kinds of things like that and they put those all together and they make music, they make a song.

Same thing here, what a higher-order component does is it composes different features and it puts them together so that you can export one wrapped up feature, so what we're looking to do just to kind of get back into a more practical sense of what we're looking to accomplish, we know we're missing history, we're missing the props.history that we'd have access to if we were a dedicated route, so our auth component had access to it because it was a route.

But our navigation component doesn't, so because of that, what we're gonna do is we're gonna compose using a higher-order component, this very cool withRouter tool and this is provided by React Router, so I'm gonna clear all of this out and let's actually add it in 'cause it's not a lot of code to add but the first time I saw this, I had no idea what in the world they were trying to accomplish and so, it took me a while to dig into it and then I realized that what they were doing was actually very intuitive but that's why I wanted to dedicate a whole guide on trying to understand this.

So, the very thing we need to do is we need to import this, so I'm gonna come up here and say import and then this is in curly braces, withRouter from and this is react-router.

import { withRouter } from "react-router";

Now note this is not react-router-dom, this is the core library, it's a more abstract concept, so it's in the more abstract library but we have access to that automatically and so, we can call withRouter. Now notice one of the conventions and just to very quickly get back, remember it said that the HOCs are not part of the React API. All that is is that's just some fancy words for saying that there's no such thing as a higher-order component in React, it's a convention people follow.

So it's really just using basic React and JavaScript in order to accomplish something but because so many people started doing this, then it became something that they even added into their documents and you will hear about higher-order components a decent amount especially as we get into more advanced concepts.

So, the convention here is your higher-order components will always start with a lowercase letter. This way you're able to see that something like withRouter is not a component. This is different than say NavLink. NavLink is an actual component, withRouter is a higher-order component.

So we're going to bring that in and then to call this, we can come down to the bottom and we're gonna say export default withRouter and then we're going to pass in NavigationComponent as the argument, so what we're doing here is we're calling the higher-order component and we're wrapping inside of it the NavigationComponent and the end result, so what actually gets exported, is a merged, a composed component that will have access in this case to the props.history.

So that was a lot of me talking, even more than usual, so let's test this out and actually let's see it hopefully working. So, we're currently in a NOT_LOGGED_IN state, let's go and let's log in and add your DevCamp Space login details. So, we have everything, looks like everything's working, I can hit Refresh and we're logged in.

Now if I click Sign Out, now you can see that it actually is working. If you open up the console, we're not getting that same error, we're getting that warning which we'll talk about later on.

Now let's also test out, we're not getting the error but let's make it's also working. So, I'm gonna say auth, let's test out the redirect, so I'll say my Login and let's say that we're on the Blog page, we're in it, we're able to see it and then it's time to sign out.

If you click Sign Out, now you can see we're redirected to the Home page, we're updating the parent app state and we are good to go, everything is now working and we're able to use the higher-order component design pattern in order to do it, so hopefully that makes sense on what higher-order components are in React and you'll have a good idea on how to use them later on.

So great job going through that, I know that that was a very long section but we now have a fully functional authentication system built into our application where we're able to log in, log out and manage different authorization rules as we're navigating through the app.

Resources