Logout and Update Users with React and Supabase's upsert Method

Share this video with your friends

Send Tweet
Published 3 months ago
Updated 2 weeks ago

In this lesson, we'll manage usernames for our live chat application by creating and managing React user interface elements that allow users to create and update a username. We'll also use Supebase's upsert method to update username's in our database.

Kristian Freeman: [0:00] With all of that, we've set up the current user variable. We can actually use it in our application by passing it as another prop from our useSupabase hook. I'll just say current user.

[0:12] We have current user session in Supabase. In App.js, we can add to our existing useSupabase hook call here by saying current user, so we'll just pull that out of the props as well. Then we will pass it into whatever component our App.js component decides to render. I'll just say currentUser=currentUser.

[0:35] In places like Chat.js, I can get access to current user by adding it as another prop here, being passed into this component. At this current user, we can actually show something in the UI to indicate that we know who the user is, that's currently signed in.

[0:51] Let's come down to the user interface. Here, inside of our header, I'm just going to make a paragraph tag that says, "Welcome, currentUser.username." It may be that, for instance, in our case, username isn't set yet. This will be no. Instead of rendering this out, let's use a ternary and say, 'Welcome to either currentUser.username if it's set. If currentUser.username isn't set, we'll use session.user.email.

[1:20] That would give us a fallback value in case currentUser.username isn't set yet. Back in the UI, if I refresh here, you can see it says, "Cannot read username of undefined." It says, "Welcome currentUser.username." It's trying to call username on something that isn't set yet.

[1:36] The reason for that is that this is trying to render before currentUser is set in our Supabase hook. It's an asynchronous piece of code. It needs to run and complete before we can obviously make use of the values inside of it.

[1:49] The easiest way to fix this is knowing that currentUser may not yet be set before this code can run. What we'll do here is say, "If there's no currentUser, return null, though, you could obviously do like a loading screen or something here." I'll leave that as an exercise for you in the future if you want to do that.

[2:07] Now, even though I refresh, you can see that there isn't anything showing up here. The reason for that is that if I come back to U Superbase, I've set up a useEffect hook without passing in a second argument here telling it where and how to run. The best way to do that is to pass in an array. Then, look for the things that we care about in here that are going to change.

[2:28] For instance, session here may start off by being null, there's no session or anything like that. Then, as the page loads, session may end up being populated. Let's put in session here and know that if session changes we need to rerun this useEffect function with our new session data. Although we've set current user here as a function prop, I actually missed one spot.

[2:49] If I come to index.js which is our index page here in the sidebar, we aren't actually getting CURRENT_USER out of this component. This is being passed in here, component CURRENT_USER equals CURRENT_USER. What we need to do is destructure it out of this prop argument here and then pass it into the chat component.

[3:11] Again, I'll just say CURRENT_USER CURRENT_USER. If you're a React expert this might be a good place to start using something like React Context, which would just make this available whenever you need it to be available.

[3:22] Though we're at a point where it's probably not worth it for just these three props. Just as sort of a note that you may start wanting to put things inside of a React Context if you're familiar with that workflow.

[3:32] If I refresh the page, you can see that up here at the top, it says, "Welcome Kristian at kristianfreeman.com." What it's saying here if you remember is that, because there's no username set we're going to default to this email value.

[3:46] The next thing you want to do is make it so that people can set their own username. To do that, I'm going to make a little setting section up here at the top.

[3:53] It's going to be able to do a couple things. First of which, update my username. It's also going to allow me to log out. There will be a log out button I can click here that will basically clear all of the application storage and send me back to the auth page.

[4:08] Here in our header for chat component, let's make a new div which we'll call, or which we'll give a class name of settings. If you copy this CSS from GitHub in this example, you already have this CSS ready to use. In case you don't have it set a div here, it's just fine.

[4:27] What we're going to do here is basically have two different states. One, if someone is not editing their username, there's just going to be a couple buttons they can click. One will say, update username and the other will say log out.

[4:40] If they are editing their username they will have a form here that allows them to update their username and then submit it back to Supabase.

[4:49] What we're going to do is we're going to setup a new useState hook up here at the top called editing username. This will just be a Boolean value so it will start off as false. Then what we can do down here in our settings is say, if we're editing our username render this form else, render a div and that will have our two buttons inside of this.

[5:14] We'll leave these blank for now, and then let's begin by implementing first this div here with the two buttons that we care about.

[5:21] The first thing we're going to have is a button. This is just going to say edit username, and we're going to give it a very simple on-click handler here, which says, set editing username to true. If I save and refresh here, and I click edit username, you can see it disappears because it's now starting to render via that empty form.

[5:41] Now, you'll notice the P and div tags were rendering in a weird way there. That's because this P tag needs to be inside of the header text div. That'll fix the rendering here a little bit, which has a down here at the bottom now.

[5:54] We have our edit username button setup, and now we need to add our logout button. I'll just copy this button again and make a new button, which just calls a logout function, and give it the text log out. Of course, this function isn't defined. Let's define that real quick.

[6:10] It takes an event in and it just calls event.preventdefault. Then it also needs to log out. There's a couple ways to do this. Supabase has one built in which is just a sign-out function. Though, I find that that can be a little buggy.

[6:27] I prefer to do it a different way, which is just to clear all of my local storage, which is how Supabase stores whether you're logged in or not in your browser. We can do that by saying window.localstorage.clear. Then also reloading the page. Window.location.reload.

[6:45] If you'd prefer to try Supabase's approach, it's just supabase.auth.signout. Though like I said, I've had issues with this in the past, but that's available as an option if you want. For now, I'll just say window.localstorage.clear, and window.location.reload.

[7:02] If I save that and then come back here, you can see there's a log out button. If I click that, I get an error here. It looks like I called event.preventdefault, but I didn't pass in the event here. That's because I use this format by copy pasting.

[7:16] Let's remove this empty function argument and grab the event argument, and pass it in to log out. Now if I refresh the page and I click log out again, you can see everything gets cleared. The page reloads. It's actually so fast.

[7:31] You probably can't even really tell that's happening, but that is what's happening here. We get sent back to the auth page. I'll just log back in. Now we know that my logout functionality works.

[7:41] Now, when it comes time to editing the username, we're going to want to put a form here, which will have a single input, which will be a new username input as well as a button to submit.

[7:52] Once that form is submitted, it will make a request to Supabase saying update this user's username value to this new form value. To do that, I'm going to come into this form. I'm going to make a new input and I'm going to just give it a placeholder for now of new username.

[8:09] Then I'm also going to do a button which says update username. Now, this form needs to have a on-submit handler of some kind attached to it, so I'll say on submit equals set username.

[8:23] Again, much like the other forms you've done so far, I'll make this required, and I'll also give it a ref which is just going to be new username. Now if I come back here and I refresh, everything looks good here. If I click edit username, it goes completely blank.

[8:38] The reason for that is I haven't set up the set username function and I also haven't set up this new username ref. Let's get started doing those now. Up at the top, I'll make a new ref called new username, and I'll set it to the same sort of thing as message, which is just an empty string.

[8:55] I'll also come down here and set up the set username function, which is async. I'll also want to grab the event here and call event.preventdefault, which will stop the form from reloading the page. Now inside of this function, we need to get the username.

[9:12] We'll say, username equals new username.current.value. Then I need to submit this to Supabase using an insert call. I'll say await Supabase from user. Then I'll say, .insert passing in an array. That's going to have two things inside of here. I'm updating my current user value.

[9:34] When I'm doing an insert on something, I actually need to pass in all of the values that I care about here. I'm going to say, current user using the spread operator, which says pass in all of the current values for current user, as well as the new username.

[9:50] All of this stuff is from my existing user information. Then I'm also going to pass in the username. Finally, because this user already exists, it will give me an error if I try and effectively insert a new user that already exists.

[10:04] What I need to do here is pass in a second argument into this insert call, which is an options object that has upsert set to true. Upsert is a combination of insert and update, meaning that it will look for an existing user, if it exists, and it will update it. You won't have to worry about getting any sort of error here.

[10:24] Once I've added the username field to my user, I can do two things. First, I'm going to say, "New username.current.value is empty." I'm going to make that an empty field again, clearing out that input. Then, I'm also going to stop editing the username, so set editing username is false.

[10:45] Now, if I come back here and refresh the page, let's try editing the username. I'll say, "My new username is Kristian," click update username. You can see it actually updates over here, "Welcome Kristian," because it updated the username in Supabase. Then our subscription, which looks for changes to our current user, saw that there is a new update and updated the username accordingly.

Joey Ng'ethe
Joey Ng'ethe
~ 2 months ago

Nice tutorial. A question, does superbase provide an API to unsubscribe to changes? It might be useful to do some clean-up to avoid memory leaks when the components unmounts. But so far, looking good; great job.