Updating Styles with React Event Listeners
In this lesson, we are gonna learn how we can build out that cool hover effect so that, when you hover over one of these portfolio item components, it darkens the background image and makes it easier to see the content in front of this, and we are going to learn about event handlers inside React.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Now, if you're familiar with event handlers in JavaScript, React is very similar, but they are gonna have some key differences, the number one difference being that, in React, we don't work with a real DOM. We work with a manipulated DOM, or a shadow DOM, and so we're not going to be directly selecting and changing elements, but if you do remember back to when we've talked about how we can work with event selectors and event listeners, there are gonna be a few similarities, so let's get started on that.

The very first thing we're gonna do is, we are going to convert this from a functional component into a class component. The reason for that is because we want to be able to manipulate a piece of state. One of the best ways to get this accomplished, to be able to have this type of behavior where I hover over, and it changes some type of CSS style, is to have a class that is added and then removed.

So what I'm gonna do is, create a name for a piece of state, and then I am simply going to remove it, and then add it back in as they hover and they move their mouse over these components, that's what we're gonna use the state for. Before we do anything, let's do that first, and it's also good practice because one common thing that you're going to be doing as you build out React applications is, you are going to have to convert your functional components into class components.

The very first step is to import the Component library from React. Instead of exporting default and exporting a default function here, we're gonna say export default class. I want to call it PortfolioItem, then it is going to extend the Component class inside of React.

Now, after that, we need to add a couple of things. We first need a constructor, so I'm going to add a constructor here, we are taking in props, so we add that as an argument. Then make sure that you call super, and we've done this before, but just so you're remembering, we're calling super here, so we bring in all of the behavior from the Component class here.

Then, before we get into adding state, let's go and fix what we have down here. If we have a functional component, we only have to return some JSX code, but if we have a class component, we need a render method. So I'll call render, and then add the start of the curly brackets, and then come all the way down to the end of the file, and add some more curly brackets. Now, this should all be working.

Now, there's one other item. If you notice, right here, where we're performing our destructuring, we're calling props, we now have to say this.props. Let's just go test this out to make sure that we haven't broken anything before we start adding new behavior.

If I come right here, I'll hit refresh just to be on the safe side, and it looks like everything is still working perfectly, so that is how you can convert a functional component into a class component. Now, let's go, and let's add our state, so we'll say this.state =, and we're gonna call this portfolioItemClass, and we'll start with this just being an empty string, just like this.

this.state = {
    portfolioItemClass: ""
};

Now, what we're going to do is, we're going to build some methods that are going to handle the event. I want to have one that is called handleMouseEnter, and it's not gonna take in any arguments. Then I want to have one that is going to say handleMouseLeave.

Now, you do not have to call these by this name, you could say anything. You could call it ASDF if you wanted, but this is a common convention in the React community. When you're working with event handlers, and you're building methods for them, the most common convention is to start your method off with the word handle because it gives a very clear signal on what this code is doing.

What I want to do, for handleMouseEnter is I want to update the state. I'm gonna say this.setState, and then pass in our portfolioItemClass. I want to call this class, and I know it's a little harder to see when that IntelliSense comes up, so I want to call this image-blur just like this, and this is gonna be a class that we're gonna be creating.

portfolio-item.js

handleMouseEnter() {
    this.setState({ portfolioItemclass: "image-blur" });
}

Then, for whenever someone leaves, so when the mouse leaves, I want to have one that says, I just want to update this so it's an empty string again.

handleMouseLeave() {
    this.setState({ portfolioItemclass: "" });
}

If you can see what behavior we're trying to implement, whenever the mouse goes over anywhere on the component, I want to have the state manage adding that class. As soon as I do this, it's gonna update state, as soon as I remove it, it is going to remove it. We can even test this out here before we go too far.

Let's go down into our JSX, and let's add our event listeners. Here, I want this to wrap around and listen around the entire div. At the end of portfolio-item-wrapper here, I'm gonna give us a little bit of room, and now, I'm going to add our event listeners.

Now, these do have to be named by the exact name I'm giving because these are the dedicated listeners that React gives us. I'm gonna say onMouse, and then I want to do onMouseEnter equals, and then we're gonna pass in some JavaScript. We're going to pass in an anonymous function, I'm going to start with the parens, and then give an arrow function, and then say this.handleMouseEnter then call it with t parens after that.

onMouseEnter={() => this.handleMouseEnter()}

Now, if you've never seen code that looks like this, that is perfectly fine. The first time I saw this syntax, it was very odd, so let's kind of walk through it before we even add the MouseLeave. This right here, where it says onMouseEnter, this is the JavaScript listener. This is what is going to listen for the mouse event over this div.

Now, the reason why we have to have the parens right here, and an arrow function before we call the function here is because, if we didn't, then this function would just run automatically, and that would cause a problem because, then, you just automatically, you'd be updating your state constantly, and you'd create this infinite loop where it is getting updated to image-blur, and then it is being set to an empty string, and you'd end up with a big error.

When you pass in this kinda syntax where you start with the parens, and then the arrow function, what this tells JavaScript is that I do not want you to run this code until this event occurs. Don't run it when the page loads, or when the component mounts instead, load it, and wait to run it, and do not run it until the event is true. So onMouseEnter, that's how that works.

Then we're gonna have one, and this one is called onMouseLeave. Then we're going to have the handler, so handleMouseLeave, just like that, let's hit save.

onMouseEnter={() => this.handleMouseEnter()}
onMouseLeave={() => this.handleMouseLeave()}

Now, let's test this out. Right above any of these, I'm just gonna create a little h1 div, and this is just for testing purposes. I'll say this.state.portfolioItemClass, and then let's close off that h1 just to make sure that this is updating properly. I'm gonna hit refresh, and right now, there we go, you see how we have those empty spots above each one of our items now?

large

That's because that's where our h1 is, and it starts as an empty string. Now, as we hover over, you can see, the image-blur has now been updated, or I should say, the portfolioItemState has been updated with the value of image-blur. Then, as soon as my mouse moves away from that div, it goes away, so that's how we're able to leverage the event listeners to update state.

So that's working, now we can remove our little debugging code here. Now we can see how we can make this work. First, in the portfolio, and I'm gonna put this code all the way down at the bottom, I'm not gonna nest it inside of the code above.

I'm just going to give the class of image-blur. I'm gonna give it a transition of one second, and then I want to add a filter property, this filter is going to update the brightness. This is not specific to React, or SCSS, or anything, brightness is a filter that we have in just vanilla CSS. I want to update this so the brightness is taken to 10% that is how we're able to darken that background image, I'm going to save that.

portfolio.scss

.image-blur {
    transition: 1s;
    filter: brightness(10%);
}

Now we have a class that is going to give us the look and feel we're looking for. Now, we need to add that class dynamically. What we can do is, right here, in the portfolio-img-background, this is the reason why I made this a div so that I can add styles to it very easily, and then remove them.

What we need to do is, for this className, instead of having it hardcoded with a string, what I'm gonna do is, now, wrap this in curly brackets so that I can use JavaScript, and we're gonna keep the portfolio-img-background as a string, but then, what we're gonna do is add on the class.

And I'm also gonna add a space here because what JavaScript allows us to do, or what React allows us to do is to slide in whatever this value of image-blur is. If I just added them together without this space right here, it would say portfolio-img-backroundimage-blur, and that is not a class we have. What I want to do is to have a space in there, so that's the reason why I'm gonna just add that directly.

Now, I can say this.state.portfolioItemClass. When it's an empty string, then the portfolio-img-background is just going to be a regular string, it's not gonna have any other details associated with it. The empty space doesn't hurt us at all.

When we are going to have this equal image-blur, then it is going to work, and it's gonna add that className. That should be all that we need to do, to get that working. Let's come back, and look at that, this is now working perfectly. If you check the final design you can see, we're getting the exact behavior that we are looking for.

So that is pretty cool, I love that effect. I was really excited when I saw that, and I wanted to share it with you, and show you how you could build it. Great job if you went through that, you now know how to work with event listeners in React.

Resources