Hackernoon logoBasic Concepts and Applications for Event Listeners in React.js by@marcellamaki

Basic Concepts and Applications for Event Listeners in React.js

Author profile picture

@marcellamakiMarcella Maki

Moving from JavaScript to React has been somewhat intimidating, because it’s easy to forget that React is just JavaScript. Differences in syntax, JSX, and the ways that information is shared among files and passed between components can be quite confusing, so in this blog, I am planning on walking through a brief overview of what event listeners look like in React and some things to keep in mind.

First things first, we should probably try to remember what event listeners are in JavaScript.

An event listener is a callback function that listens for (or is called upon) the creation of an event object.

Let’s break that down:

One of the most powerful features of JavaScript is that it can accept a function as an argument. A callback function is the name of a function that does just this — accepts another function as an argument. When I was first introduced to callback functions, I conflated them with closures and while there are some important concepts that are relevant to both (particularly when we think about scope and this), they are also different! Here, I’m going to focus on callbacks in the context of events. Why are callbacks great?

  • It allows us to write functions that we might not need right away — for example (and as we will see here) when we are waiting for some input from the user.
  • It allows for separation of responsibilities and helps break out code into smaller bits
  • Hopefully we also take advantage of these benefits and use it to make our code more readable for ourselves down to line or other developers who may be working on the project.

When we pass an argument as a function, we need to remember to not call it until we are ready to use it! Otherwise our code may break or at least not work as expected.

The browser is constantly listening for user input or interaction. This could mean something like a keystroke, a button click, or a form submission. Upon that interaction, let’s say a click, a new event object is created based on that specific interaction with certain attributes. These attributes include things like, well, what exactly was clicked on, the location of the mouse, and are useful because the new event object encapsulates that specific interaction and allows us to reference it later.

So how do callback functions in JavaScript and react compare?

All of the concepts are the same, because React is JavaScript! Here is the syntax in comparison.

//JavaScript 
button.addEventListener("onclick", sayHi)
sayHi() {
console.log("Hello World!)
}

So, in the above example, we have

  1. added an event listener to a particular button
  2. indicated that we are waiting for the button to be clicked
  3. told the program that when that specific button is clicked (more specifically, when an event object is created for that particular click), we should call the function sayHi
  4. sayHi will then print the string “Hello World” to the console
//React 
<Button onClick={this.sayHi}/>

In this example,

  1. a button component has a synthetic event listener for a click event
  2. when an event object is created on that button, the function sayHi will be called
  3. the this. indicates that this function has been defined on the same component and is being passed in as an argument (it’s a callback)

Not too bad, right!

The Gotchas (well, “got me”s)

Event Listeners Need to be Set Up Like Callbacks

This seems pretty obvious, but the component that is listening for the click actually has to have the event listener that accepts a reference to the other function as an argument. We just went through this for several paragraphs, so you think I would have immediately understood it. However…Due to naming conventions in react and the nature of callback functions, there were obviously, two related functions that needed to work together to make the event listener happen.

We often see code like this

Content from Learn.co curriculum

In my React Panic, all of the logic I worked so hard to learn in JavaScript flew out the window and the only thought that was running though my head was “BUT WHY CANT I JUST USE HANDLE CLICK ALONEEEEE”

Past Self: WHY CAN’T I JUST SAY “HANDLE CLICK” WHENEVER I WANT!?!?!?!?!? Current Self: UMM CHILL OUT. You know that is not how event listeners or callbacks work…..

I honestly wanted to write code that looked like:

import React, {Component} from 'react';
class CoordinatesButton extends Component {
handleClick = (event) => {
this.props.onReceiveCoordinates([event.pageX, event.pageY])
}
render() {
return (
<div>
<button handleClick={this.handleClick}>Button</button>
</div>
);
}
};
export default CoordinatesButton;

Not my best idea. onClick is special! It is an event listener! handleClick is not. It could be named reactIsTrickySometimes and would still work!

Really what’s happening is just like in regular JavaScript, we write the function that determines what we want to happen after the click. Then, we pass in the function reference to the click event. It’s all basically the same thing.

Sometimes Functions (Especially in Relation to Events) Need To Be Passed Around as Props

WHAT DOES THAT EVEN MEAN!?!

Sometimes, you might write a function in one component, but the actual click might need to listen to a child component.

//PuppyList.js
render() {
const puppies = this.props.puppies.map((puppy, index) => <Puppy name={puppy.name} showDescription={puppy.showDescription} key={index} clickFunction={this.handleClick}/>);

return <div> {puppies} </div>
}

handleClick = (event) => {
this.props.puppies.map((hog) => {
if (puppy.name === event.target.name && puppy.showDescription === false) {
return puppy.showDescription = true
} else if (puppy.name === event.target.value && pupp.showDescription === true){
return puppy.showDescription = false
}
})
//Puppy.js
const Puppy = (props) => {
if (props.showDescription === true) {
return (<div className="puppyCard" onClick={props.clickFunction} ><h3>{props.name}</h3></div>)
} else {
return (<div className="puppyCard" ><h3>{props.name}</h3><img src={url} name={props.name} alt="" onClick={props.clickFunction}/></div>)
}
}

It seems super scary and confusing. It is also a lot of lines of code for an example. How could this even work? What is happening?

Let’s walk through it line by line

V. high tech wireframe

PuppyList is the parent component of Puppy. Right now, it has two jobs, which it carries out via functions:

  1. render() which has a) set a const puppies and used the map function to iterate through the list and build each <Puppy /> component with the props that we’ve decided (including clickFunction={this.handleClick} which is important, and we will return to it shortly) and then b) then returns a <div></div> with those { puppies}
  2. handleClick, our now infamous but not actually that confusing event handler, which is just telling our program what to do when the click happens

Puppy is the child component. We passed it some props, specifically here where we are constructing it in PuppyList

//PuppyList.js 
...
<Puppy name={puppy.name} showDescription={puppy.showDescription} key={index} clickFunction={this.handleClick}/>
...

But, wait a second, now in Puppy we have

<div className="puppyCard" onClick={props.clickFunction} ><h3>{props.name}</h3></div>

onClick={props.clickFunction}

!==

clickFunction={this.handleClick}

So what happened!? It’s pretty simple.

clickFunction is a prop which we set in PuppyList to be passed down to the children (all the puppies!!!!). In PuppyList, we are setting clickFunction equal to this.handleClick. By passing it down as a prop, we can say, “Hey, when the user clicks on you, go find your prop “Click Function” — and that prop tells us that it’s actually a reference to this.handleClick.

Again, this is just the basic principles of our callback function in action.

But, as this code is right now, it’s not quiteeee going to work yet.

We have to bind this so it doesn’t get lost.

“This” is lost. I am a lost. Everyone is lost!

What the $@#*&#@ is up with .bind(this) in React

When we bind this, we are working with the concepts that are important for understanding how callback functions work.

Originally, when we wrote handleClick() in PuppyList, this referred to PuppyList. Makes sense!

Now, though, we are sending the reference to handleClick. Think back to regular JavaScript. Since the function is now “nested” inside another function, the scope changes from the original scope, to just the scope of the function it is called within. ‘“handleClick()” is living inside of “clickFunction” who has NO IDEA that “this” is supposed to mean PuppyList.

Hmm… There must be a more fun way to think about this.

Here is a lizard in a tank. Originally, I was going for a lizard in tank as in terrarium, but 1. this is a lesson in lexical precision and 2. this is way funnier and perhaps proves the point even better.

Right now, our lizard is in the tank, and it’s keeping him nice and safe. When the lizard is in the tank, he is not going to get squished or break. Yay! Let’s assume that the tank doesn’t actually drive but has to hang out in the lizard habitat, and if he leaves to go on an adventure, he will have to venture out without any protection. This is not great! This is not safe! When our this loses context, it is not safe — it is all alone and confused. We don’t want that to happen.

Here is a SUPER HAPPY turtle. Why is this turtle so happy? Because no matter where she goes, she brings her armored tank with her. She is safe and protected and way more bada$$ than that lizard.

This is what happens when we .bind(this). We want our functions to carry their context with them so they don’t break!

Okay, so maybe, now we should see this in action.

class PuppyList extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);

}
render() {
const puppies = this.props.puppies.map((puppy, index) => <Puppy name={puppy.name} showDescription={puppy.showDescription} key={index} clickFunction={this.handleClick}/>);

return <div> {puppies} </div>
}
handleClick = (event) => {
this.props.puppies.map((hog) => {
if (puppy.name === event.target.name && puppy.showDescription === false) {
return puppy.showDescription = true
} else if (puppy.name === event.target.value && pupp.showDescription === true){
return puppy.showDescription = false
}
})
}

Lines 2–4 pass along props, and reassign the value of our this.handleClick function to a version that carries this along with it.

Write turtle code! Don’t write lizard code!

Conclusions

Reacts can definitely be confusing at times, but at the end of the day, it’s just JavaScript with some weird magic thrown in. Don’t worry too much about the magic. Think about things in terms of regular JavaScript logic. If you can’t figure out quite what’s going on, or how this would happen with JavaScript, check out the docs, maybe there is some stuff going on under the hood that’s a little tricky, but you can probably figure it out.

Resources

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.