Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
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
- added an event listener to a particular button
- indicated that we are waiting for the button to be clicked
- 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
- sayHi will then print the string âHello Worldâ to the console
//React
<Button onClick={this.sayHi}/>
In this example,
- a button component has a synthetic event listener for a click event
- when an event object is created on that button, the function sayHi will be called
- 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
PuppyList is the parent component of Puppy. Right now, it has two jobs, which it carries out via functions:
- 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}
- 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
Basic Concepts and Applications for Event Listeners in React.js was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.