7 days of WordPress plugins, themes & templates - for free! Unlimited asset downloads! Start 7-Day Free Trial

Next lesson playing in 5 seconds

  • Overview
  • Transcript

4.14 Creating the User Profile

The final component that we need to create for our application is our user profile view. We've set up a route for this although we haven't actually referred to the user profile component yet, because we haven't created it, and we've linked to the route from several places. So it's time to actually create this user profile class. So I've created a new file here inside source/components. And of course we'll start by requiring react, as you might expect. And we'll also pull in the UserStore and the ChirpStore, because we're going to need show some information from both of those. We're also gonna want to show the user's avatar, so I should pull in the utils file as well. Finally, if the user is viewing someone else's profile, it's possible that they want to follow or unfollow them. So I will also pull in our follow button. All right, now let's create the UserProfile class. We'll start as usual with the getInitialState function. In this case, we want to get two things. First, we want to get the user whose profile we are trying to view. But how do we know which user that is? Well, if we come back to the main.js file, you can see that the bottom route here is the one we're creating, the one named user. The path is user/:id and of course that is a token. This is where we find out which user we want to display by looking for the ID in the path. While were here let's change our handler here, so that instead of components/home this is components/UserProfile. All right. So how exactly can we get this parameter out of the path? Well, the way React Writer gives this to use is through this .props.params.whatever we called the token. So in our case, this is params.id. So let's go ahead and convert that to a number. We'll say id = parseInt{this.props.params.id. So then in our return statement here, where we are trying to get our user, we can say UserStore.get and we can pass in that ID. Then the second property that we want to get is the user's chirps. So we're gonna say ChirpStore., well we don't really have a method for this yet. Somehow, we want get the chirps by the user's ID, so let's create a method here called byUserId and pass it that same ID. Now right now, our ChirpStore does not have that. So let's go ahead and add it right away. So we want to open up src/stores/chirps.js and at the bottom here, we want to create our function called byUserId. And of course this function takes that id as its argument. And this is actually very simple. We can just return this._data.filter. And all we have to do inside this function is compare chirp.userId and how does that match up with the id that was passed in. So we'll only return the chirps with the same user ids as the user id that was passed in. There we go. So that was quick and easy, and now we have only Chirps by that UserId. All right, so let's look at our render function now. So inside our render function here, we'll start with a general wrapping div. And then inside this div, we can start with the user's image. So we'll create an image tag, and let's add a class name here of two columns, so that's how big the image will be. And then we'll set the source equal to utils.avatar, and we know that we need to pass this avatar function an email, so we'll say this.state.user .user.email. And then let's close out our image tag there. Then, underneath this, we'll create a div, and it will take up the remaining ten columns in our row. And in here, we'll start with an h1. Which will just have this.state.user.fullname. And underneath that let's create an H3. And that will have this state.user.username, so we can see both the users full name and their user name. And why don't we add a class name of timestamp to this. It's not really a very semantically named class, but we know that this makes things just a little bit lighter shade of gray. So that I think will look good there. Finally, underneath this, let's add our follow button. So if the user wants to follow this user. They can do that. So that will be follow button and then we want to pass in a user id. And that will be this.state.user.cid. And don't forget, follow button is a self closing tag. Finally, at the very bottom of this, let's just create an unordered list. Which will display the chirps of this user. We're not going to use our whole Chirp Box as we did before, we'll just create a very simple set of list items. So let's create these chirps up here, and we can do this .state.chirps..map and we'll just keep this really, really simple, we'll just a list item. With, chirp.text inside of it, nothing more, just those chirps. And I think that's all we need to do. We should be able to successfully click on one of our user profile links and see their profile. So if we come back to our web application and refresh the page, you might recall that inside these boxes we've created, the avatar here is a link to their profile page. So if I click this avatar, wow, okay, that's not what I expected. We were taken to a different user's page. If I go back and click on the other one. That takes us to the right page, however, we are not seeing our list of Chirps. Okay, so let's start by correcting these links. Right now, if I hover over links on this page I can see, if I look down in the lower left hand corner here, that this has a user ID of zero, this has a user ID of one. For some reason, this user has a different ID. Let's take this from a different perspective. If we go to the users page here, we can see that this has a user ID of two, that's correct, and this has a user ID of one. Okay, so lets see what we can do about this. Now the place where this error is occurring Is in our Chirp Box, its right here where we have this link object surrounding our image. If we look at the params here, this is where we had to do that little bit of a hacky thing where we said first use the user ID or if its not there use the CID. And I think the problem here is that our first user has an id of zero, which is an acceptable id in our system. However JavaScript considers it a false evalue, and so we're skipping that user id in that case. So what I'm going to do is do this above our return statement. I'll say var id. And we can make this a ternary expression. Let's say if type of user.userId equals a number. So if that userId property exists and it is a number, then we'll use user.userId. Otherwise we can use user.cid. So this way, we're not depending on user.userId being a truthy value, we're actually checking to see that it is a number. So now, inside of params here, I can change all of this to just be the id. Let's see if that fixes our problem. If I come back and refresh the page, now as I hover over these, I can see that both of these have a route to /user/0. And if I click on one of them, hm. So now we can successfully see the right user's profile page. However, there are two things that I'm worried about here. First of all, we're getting a Follow button. We should not get that here, because we're viewing our own profile page, so we shouldn't be able to follow and unfollow ourselves. Of course, this does work. So, you can see, if we do followed here. You can see in the data that's returned, we have our object with a CID of 0 and now it's following someone with the ID of 0. And of course, we could even unfollow ourselves. And later on, you can see that, now we're only following ID of 1. However, we don't even want to allow that. That means we have a bug in our follow button. If we come to our follow button here you can see that on line 22 we're comparing this dot state dot ID to this dot props dot user ID. Well, we know that we're passing in the right property, so what's going on with the state? If we look back up at this state here you can see the ID comes from userstore.currentuser.cid and not .id. There we go. Now, if we come back and I'll refresh the page, I'll click on our user again, you can see we get the text this is you. Finally we're not seeing any of our chirps. So let's check that out. And if we look at where we're mapping over our chirps file, you can see it's very simple. We just forgot to return the list item. So if we add our return statement in there, and if I come back and click on a profile again, you can see now we have our chirps showing up down below. Excellent. Of course, now we're getting this react error that each child should have a unique key. So I'll go ahead and add our key to a list item here and that will just be chirp dot CID. Now, there's one other problem that we're going to have, and that is, what if I'm on the page for this user and I go ahead and refresh the page? Notice that, all of a sudden, nothing shows up on the page and we have cannot read property email of undefined. And again, this is a similar problem to what we had earlier. Where, when we're trying to render the page, the user data has not yet come from the server. So the first thing you might think we have to do to fix this is to add our componentDidMount: function. So if I go to, let's say UserList, and I'm gonna copy componentDidMount, componentWillUnmount and onChange. And I'll just copy those and paste them above render. Now, that's a good start, however our user profile page is using more than just the userStore. It is also using the ChirpStore. So inside componentUnmount, I'll duplicate the line that says we want to listen for changes on userStore, and I'll change this second one to chirpStore. And then I have to do the same thing for componentwillUnmount. This way we're listening for changes on both the user store and the chirp store. So if I refresh the page that was a good start but it doesn't actually work. The reason it doesn't work is because think of it this way. The user goes to this page and the react classes try to render. Now we don't have that data loaded yet, but they'll go ahead and try to render anyway. What we want is for basically that initial render to fail and then milliseconds later when that data comes from the server. These actions will come through our system. We'll say we've got the chirps, we've got the users. And now you can successfully rerender this page. And the user will hardly notice that there were two renders going on there. It will just basically see here's the data that you asked for. However, the problem is, when that first render that we want to fail happens, the whole system stops. It says we can't read property email of undefined. We can't get that user object. We don't know what to do. So what we have to do, basically, is provide it with enough data so that these failures won't actually cause the system to crash as we have. This may sound complicated, but it's really easy to do. Here in get initial state where we're creating our user object here, we can say UserStore.get user by ID. However, if that does not return something useful, then we'll just replace undefined or whatever we get returned with an empty object. That way it has an object that it can look for these email and other properties on. Now those properties will just be undefined, however, it won't complain about that. It only complains when we try to get a property of an undefined object. Now you might wonder, do we need to do something similar for chirps? And we don't actually, because byuserid will return an empty array if there are no chirps for that user yet. So that will work just fine. So now our initial render will fail, however, a subsequent render will work. We could actually see this in action if we put a console dot log in our render function here, I can just say user profile rendering. And now if we come back and refresh this page, you can see that everything worked just fine. Notice that it actually rendered three times, because we have two actions that we're listening for. We tried to render it first, that failed. Then we rendered with our chirps and then we rendered with our user information. But that all happens so fast that the user just thinks they're seeing the page as it was loaded the first time. If I go back to Timeline, and let me refresh the page. And now, I go straight to a user profile, you can see that since all that data was already existing in the browser, the user profile was only rendered once. And there we go. That is the end of our user profile feature.

Back to the top