As you add to your application, you'll begin to build complex state representations that need to be managed. In this lesson, we'll manage a user store representing all the users in our chat application.
Kristian Freeman: [0:00] We've made it possible to get the username information for our user and then update it using a edit user name form. Though up until now, we still don't have any information in our actual message view about who these messages are coming from or even when they were created.
[0:16] What we need to do is two-fold. First, we need to get all of the user information for our chat application. We want to get a list of every user, as well as their username, their ID, and then we want to use that to show the message username is, for instance, Kristian or someone else, etc.
[0:34] Then, whenever new messages come in, we also want to check and see, is there a user sending this message that we don't know anything about locally?
[0:42] For instance, if a user signs up, joins our chat, and then sends a message, it may be that we don't have all of these are information that we need to be able to properly show that message, what'll need to do is build some system for requesting new information as we need it from our Supabase API.
[1:01] All of this in total represent a more complex state interaction in our application. We need to begin looking at how to store a lot of data in our application and make requests as needed in a performant way. We'll take a stab at that here by building out some more complex React hooks and starting to make these components a little bit smarter.
[1:22] The easiest way to build this would be for us to come up here into this useEffect hook where we've gotten our messages. You can see here, we selected everything from the message table. We set up a subscription. We could do that, but it actually wouldn't be the smartest or most performant way to do this.
[1:38] Instead, what we want to do is just get the user information for the messages that we currently have in our system. Let's say that I, as Kristian, send a message and you, as for instance User 2, send a message, I want to make a request to Supabase that says just get the user information for the users that are currently inside of our message data.
[2:00] To do that, I'm going to make another useEffect hook. Inside of that, I'm going to make a new function called getUsers, which is going to be an async function. Inside of that, I'm going to get a list of user IDs. I'm going to say const userIDs and this is going to be messages.map. Run through every message in the array and return message.userID.
[2:44] I'll just say new set from this array of user ID messages or from user IDs in our messages array. Then what I want to do is I want to use the set of user IDs to make a request to Supabase. I'm going to say await get users from Supabase.
[3:03] Then I'm going to pass in two things here. First, I'm going to pass in users, which is something we haven't made yet, but we will in just a sec, and then user IDs. This is going to be the set of user IDs that we just defined for all of the user IDs inside of our messages array.
[3:21] Then finally, I'll set this to maybe new users, which will be a variable. I will say, set users to new users. In case the set users pattern looks familiar, it's because we're going to use use state again to make another variable here.
[3:38] This will be users and set users, and we'll just say use stay here. Instead of doing an array, as we've done in the past, we're actually going to do an object and you'll see why a little bit later on. Back down in our useEffect hook, we've set up get users.
[3:53] Now, we're going to actually call this by saying await get users. This will be a function that we will call. Then in the useEffect hook, we need to pass a second argument that says when to run all of this code. In this case, we're running it based on messages.
[4:09] We're saying create the set of user IDs based on the message array. Let's pass in messages here and say any time that this messages array changes, or be run this function. Now we can move on to this get users from Supabase function.
[4:25] Let's make a new function here, get users from Supabase. This will be an async function that takes users and user IDs. Now inside of this function, what we want to do is basically look at the users that we already have.
[4:41] This is going to be an object containing a bunch of users. Then we have a set of user IDs which is going to be all of the user IDs that we know about from our messages. What we're going to do with those is we're going to look for any new user IDs.
[4:55] Any users that are sending messages that we don't already have stored in our local component. We're going to make a request to Supabase to get that new user data just for those new users. We're going to make a new variable called users to get.
[5:10] This is going to be a list of user IDs that we don't know anything about, things that are not inside of our users object. I'm going to say array.from user IDs. Remember that user IDs is a set here and array.from is a way of just taking that set and putting it back into an array.
[5:30] I'm going to filter based on that array and say, filter out anything that is inside of my users object. What I'm saying here is look for an existing instance in the users object, the user ID. If we already have this user stored, then filter it out of this users to get.
[5:55] What I'll have here is an array of of IDs that we don't currently know anything about in our local component. That's what I'll use to make a request to Supabase to get those new users for storing in our component. There may be a situation where we have an update here.
[6:12] If someone adds a message and we already know about their information, this function might run and we might find out that we don't need to actually make any requests to Supabase to get new user data.
[6:23] This will happen if the object keys.length for the users object is not zero. If there are users inside of this object and users to get.length is equal to zero. Again, this means get the length of users inside of the users object.
[6:46] If there are any users inside of this object, but the users to get length is zero, that means that we know about all of the users that we currently have in the system and we don't need to do anything further.
[6:58] We can just return users here and just return the data that we already have. We don't need to make any additional requests here to Supabase.
[7:05] Now if that's not the case, we have a list of IDs that we need to actually make a request to Supabase to get and that will be our new user data that we need to add to our existing data. To do that, I'll say await Supabase from user.
[7:20] For the SELECT here, I'm just going to select the fields that I care about which are ID and username. You may actually have other fields here. Normally, a user table tends to be stuffed full of all kinds of information. You may care about some of it here. You may not.
[7:33] In our case, I just care about ID and username because I'm using this to populate the username field in our user interface. I'm just going to select those fields that I care about.
[7:43] Then I'll also use the in operator which allows me to provide a key ID, and then a list of IDs which will be users to get. What this will allow me to do is say, get all of the users from my user database, select the ID and username if the ID is within this array of IDs that I want to get back.
[8:04] This will return an object, which has data inside of it. Those will be our new users. Of course, this will be an array of users that comes back. I need to turn this into an object.
[8:15] I'm going to make a new object called new users. Then I'm just going to loop through the data array, same data for each. This will be a user that will be each function here will have a user passed in and I'll say, new users user.ID equals user.
[8:35] I'll loop through everything in this data array coming back from Supabase and populate a key value pair in my new users object with that user data. Then finally, I'm going to return a object back here which represents the complete user object.
[8:53] I'll say, return object.assign which allows me to make a new object based on a couple different objects. From left to right I'll say, an empty object, then apply users onto that, meaning that it will copy all of users into this empty object.
[9:11] Then it will also add new users which is an object that has all the new data from Supabase. This will return a users object that contains all of our old data plus our new data, which we can use to update our user interface.
[9:27] Down here in our useEffect hook, since we're already taking this new users object back from this function, I can then use set users. This will just update the users object with any new changes, when anything changes in the messages array.
[9:44] Now that we have all of this setup, we'll have a users object, which we can use to look up based on the user ID for a message and show the username for a specific message.