FREELessons: 11Length: 1.7 hours

Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

5.1 Events and Passing Functions

Events are the lifeblood of any graphical user interface. As such, you'll need to know how to use events in React. It's easy, really—there are just a few things you need to be aware of.

5.1 Events and Passing Functions

Events are the life blood of any graphical application. And so as you are writing your React components there are going to be some times when you want to leverage events. So in this lesson, we're going to be looking at events in React. And really I'm not going to be teaching you anything new because events work pretty much how you would expect them to. There are some things however that you need to be aware of, so that's what we are primarily going to focus on. However, we are going to be writing a lot of code. All of this stuff is going to be from scratch, so we are going to be spending some time with that. So this is the component, or really the components, that we are going to create. We have a menu component which is the conglomeration of everything that you see here. And then we have individual menu items. So let's say you're walking into your favorite sandwich shop and you want a meal. So today you want a sandwich and a drink, so you click on those two items. You see that they change color and then you can see that the total updates. But then you think, well, no. I just want a sandwich and chips. Then you can click on those items. You can see that chips was activated. Drink was deactivated and the total updated. So this is what we are going to write. We have two things. The first is the menu, which encapsulates everything there. And then we have individual menu items. And both of these classes are stateful. The menu keeps track of the price or the total. And then the individual menu items keep track of their own state. So we want to extend React.Component. We're gonna start with menu and whenever we create the constructor, we will then copy and paste this for creating the menu item class just to save some time. So we're going to accept our props, we want to call super, passing in props. And for the menu we want our state to be the total. So that is what we are going to have. We will initialize that as 0 and there we go. Let's go ahead and write the render method as well, just so that we have that there, because both of these components are going to need the render method. So let's copy and paste. Let's change the second class to MenuItem. And as far as the state is concerned, our property is going to be active. Now I have this menuItems array. And you can see that we have our three items. We have Sandwich, Chips, and Drink. And each one of this objects has a name and a price. So we are going to pass our menuItems to our menu. And we will have a prop inside of our menu component called items. So let's go ahead and render those, we'll say menuItems = and then we'll say, this.props.items.map, and that's a mouthful. And we want to work with the individual item as well as get to the index, because as we are iterating over these and creating our components, then we want that key prop as well. So we're going to have a MenuItem, we'll go ahead and set key={index}, and then we could say, name={item.name}, and then we could do the same thing for price. However, we could also do this. We'll use the new spread operator and then just say item. That's going to deconstruct the item object into individual values. So this is essentially saying name = item.name and then price = item.price, because those are going to be the property names inside of our menuItem. Now if our menuItem had different names for those props, then we couldn't use this spread operator like this. We would have to explicitly say prop = item.name, prop = item.price. But since they have the same name, we can take a shortcut and we're good to go. Okay, so as far as our menu class is concerned, this is all that we are going to be doing right now. Eventually, we will need to update the total. But when it comes to our menuItem, let's go ahead and render that. Now we also need to take into account whether or not the item is active. Because we have a CSS class called active, and of course if an item is active we want to use that class. So we're going to build the class or classes that we are going to assign to this element. And then we will use that variable. So let's just call this className and every menu item is going to have a menu-item class. But if the state, or the active rather, is true then we are going to say className += and we are going to add the active class there. So that's whenever it comes time to render this and we didn't return any components in the menu component, we'll have to do that. But let's go ahead and finish this since we are here. We're going to say className={className}. And then we want to display the name and the price. So we'll just say this.props.name, then in parentheses we'll have this.props.price. And that's going to be enough for right now. So let's go back to the render method in our menu. Because we built our menu items, but we didn't build the rest of our component. So we're going to return here, and we're going to have a div containing everything. And then we're going to have another div with a class of menu. And ideally we would be using an unordered list or some type of list, because semantically that's essentially what we have. We have a list of menu items. But unfortunately I didn't really think of that until after I had prepared this and started recording, so we're just going to be working with divs. So inside of this div element, we're going to put in our menu items, so there we go. And then we're going to have our total, so let's, do we have an ID or anything for total? No, we don't. So we'll just have a div element and we'll say Total, and then this.state.total, and that will work. So if we go to the browser and refresh the page there, then everything should at least look the same. And let's inspect here. We have menu-item inside of a div. We did not give a div a class, yes, we did. And that's the problem, though. We didn't say className. So that still gets me. And it will probably still get you. So there we go. Now of course, if we click on any one of these, nothing happens because we haven't set up the click event. So let's go ahead and do that. We're going to set the click event on our menuItem on the div element. Now this is how we do it. We don't say document add event listener or anything like that. Instead we say onClick. So it's a lot like DOM Level 0. And then we assign our clickHandler. Now, to note here, casing of this, we have on in lowercase and then we have an uppercase C. This is important. If you say onclick, all lowercase, then it's not gonna work. You will however, get a message that says, did you mean onClick with a capital C? And then you'll say, yes, of course. So there is some casing here that you need to be aware of. So onClick, and then we just say this and then whatever we want to handle the click. In most cases, you would probably see something called clickHandler, so let's just call it that. And then we will need to write that method, so let's do that, clickHandler. And all we are going to do right now is modify the active properties. So we're going to say this.setState(). And we're going to set active equal to the opposite of what active is right now. So we'll say not this.state.active. There we go. So let's go back to the browser. Let's pull up the developer tools, because this is not going to work whenever we click on an item. Uh-oh, Uncaught TypeError: Cannot read property 'setState' of null. So this is here. Whenever we call clickHandler by clicking on one of our items and this.set state, well, this is null and this is normal. This is typical JavaScript stuff here. Because whenever we are setting up the onClick event handler, we are simply passing the pointer to the clickHandler function and that clickHandler function is not bound to this particular menu item object. So unfortunately, inside of our constructor, we have to do that. We have to say this.clickHandler = this.clickHandler. And we're going to bind it to this. Yes, this is not ideal. There are many people that don't like this, myself included. But this is unfortunately what we have to do if we are writing our component as a class. And in this case, we need to because we want to maintain state. So here we are setting up clickHandler = clickHandler. We're binding to this. So now whenever we go back to the browser, the clickHandler that is assigned to onClick is now bound to these individual items. So if we click on this now, it's going to work. And we can see that the state is updated because the style changes whenever we click on these items. Well, now we want to update our total, and that means that we need some way to communicate between our menu-item component to our menu component. Because we are clicking on the menu items that then needs to tell the menu hey you need to update this. And that's really easy to do. We're going to start in our menu component. And we are going to add a method called updateTotal. We are going to accept the amount that we want, and then we are simply going to update our state. So we'll say this.state.amount = Although that's not what we need to do. We need to say this.setState and then we will say total and we are going to take the existing total, so this.state.total + amount, and there we go. So then whenever we create our menuItems, we will pass along that updateTotal method. So we could do something like this. UpdateTotal=(this.updateTotal). So this is going to create a new prop on MenuItem called updateTotal that is going to point to the updateTotal function that we can then call inside of our clickHandler. Now you might be thinking, well, are we going to run into the same issue that we did with the clickHandler? And the answer is yes. So we need to come up to the constructor. We need to say this.updateTotal = this.updateTotal .bind(this). So now inside of our clickHandler, We can update the total. So we'll say, active = not this.slate.active. So now we can call this updateTotal. We will pass in the amount, but the amount differs based upon whether or not the item is active. If it is active, then we'll say this.props.price because we are adding that price. Otherwise, we'll say -this.props.price. And then we will set the state equal to our variable of active. And since those have the same name, we can just use the shorthand. And so that should be it. Let's go back to the browser, let's refresh. Well, let's click on these things, something goes wrong. This.updateTotal is not a function. And that makes sense, because it's not a function, it is a prop. So we'll say, this.props.updateTotal. Let's refresh. And now whenever we click, but it looks like it's concatenating and that's definitely not what we want. And that is why these should be numbers, not strings. So let's go back, let's refresh the page, and then there we go. Whenever we click on the items, we can see that the total updates. The state of the item is maintained. And we have a functioning component. So whenever you want to use events within your components, and really it's not just events, if you want to pass other functions to other components, you need to make sure that if you want to use that function as if it's a method on an object, you need to bind that function to the object that you want it to be bound to. It unfortunately adds an extra step, but really it's nothing new. We've been needing to do this type of thing for a very long time.

Back to the top