You don’t know event handling in ReactJS
You don’t know event handling in ReactJS
Even though I’ve been working with React for a while, there are some concepts that I don’t understand why and how they work behind the scene. One of them is event handling in ReactJS, all I’ve done so far when dealing with them are some “best practices” or “ways to handle it correctly” which I found out on Stackoverflow and some Conner on the Internet. But I think that it’s time to dive deep into it and understand why we need to do “this” or “that” to make it work. Okay, let’s get started.
First, let’s begin with some basic examples of how we usually do when dealing with event handling in ReactJS.
#1
export default function App() {
const handleOnClick = () => {
console.log('clicked');
}
return (
<div className="App">
<button onClick={handleOnClick}>
Click me
</button>
</div>
);
}#1
export default function App() {
const handleOnClick = () => {
console.log('clicked');
}
return (
<div className="App">
<button onClick={handleOnClick}>
Click me
</button>
</div>
);
}What if we want to pass a parameter(s) to onClick? One of the most common approaches would be the following (I usually use this way and I think that it’s the way developers at the beginner level like me deal with it - at the end of the day when you do the google search for the way of dealing with passing parameters to the event handler function, you’ll end up with this approach)
#2
export default function App() {
const handleOnClick = (para) => {
console.log('clicked', para);
}
return (
<div className="App">
<button onClick={() => handleOnClick('test')}>
Click me
</button>
</div>
);
}#2
export default function App() {
const handleOnClick = (para) => {
console.log('clicked', para);
}
return (
<div className="App">
<button onClick={() => handleOnClick('test')}>
Click me
</button>
</div>
);
}So have you ever wondered: Why do we need to use the arrow function?
To answer this question, firstly, let’s use normal function and see what will happen
#3
export default function App() {
const handleOnClick = (para) => {
console.log('clicked', para);
}
return (
<div className="App">
<button onClick={handleOnClick('test')}>
Click me
</button>
</div>
);
}#3
export default function App() {
const handleOnClick = (para) => {
console.log('clicked', para);
}
return (
<div className="App">
<button onClick={handleOnClick('test')}>
Click me
</button>
</div>
);
}You’ll see that on the console tab of the browser, there will be at least one line showing the message “clicked test”, even though we haven’t clicked on the button at all and even if we click the button, nothing shows up anymore. The answer for this behavior is that if we pass handleOnClick as we did in the #3 example, we don’t pass a reference of that function but a call to that function immediately, so as soon as our component is mounted, the handleOnClick will be executed right away. After that, if you try clicking the button, nothing will happen because the function has been executed and your click event doesn’t have a handler function. Now back to our normal way, by using the arrow function, we passed to onClick an arrow function which returns a function, basically, we just init a new function and pass its reference to our onClick event. The point here is that we have to find a way to pass the reference of the function but not execute the function. Arrow function helps us with that.
I think that using the arrow function is one of the most common ways to deal with event handling which needs additional parameters. but this approach has one downside, each time our component is rendered, a new function is generated. So what can we do to resolve this?
The first thing we should notice is that, whenever we pass a reference of a function to handle an event in React, React will automatically pass the event object as a parameter to your function. Some might doubt that right. Let’s try it out.
export default function App() {
const onClick = (event) => {
console.log("event", event);
};
return (
<div className="App">
<button onClick={onClick}>Click me</button>
</div>
);
}
export default function App() {
const onClick = (event) => {
console.log("event", event);
};
return (
<div className="App">
<button onClick={onClick}>Click me</button>
</div>
);
}Now, try clicking the button and check the console you’ll see the event object
But wait, why don’t I see that object when using arrow function?
export default function App() {
const handleOnClick = (..args) => {
console.log("args", args);
};
return (
<div className="App">
<button onClick={() => handleOnClick(1)}>Click me</button>
</div>
);
}export default function App() {
const handleOnClick = (..args) => {
console.log("args", args);
};
return (
<div className="App">
<button onClick={() => handleOnClick(1)}>Click me</button>
</div>
);
}You’ll see that:
args [1]args [1]Hey, I only see the parameter I Where does the event object go?
I said that the event object will be passed into the function which you passed its reference to our event handler function. So when you write
() => onClick(1)() => onClick(1)Let’s call this function is A
As I mentioned above, it returns a function, so when a click event is triggered, React calls the event handler function like this: A(event).
Let’s do an addition step to see how we can get the event object
export default function App() {
const handleOnClick = (..args) => events => {
console.log("args", args);
console.log('events', events);
};
return (
<div className="App">
<button onClick={handleOnClick(1)}>Click me</button>
</div>
);
}export default function App() {
const handleOnClick = (..args) => events => {
console.log("args", args);
console.log('events', events);
};
return (
<div className="App">
<button onClick={handleOnClick(1)}>Click me</button>
</div>
);
}Some of you might not be familiar with the way that we’ve just written the function:
handleOnClick = (...args) => events => { // some code }handleOnClick = (...args) => events => { // some code }It's called currying. Since it's out of the scope of this post, you can go here to read about it.
Basically, it's equivalent to:
const handleOnClick = (..args) => {
return events => {
console.log("args", args);
console.log('events', events);
}
};const handleOnClick = (..args) => {
return events => {
console.log("args", args);
console.log('events', events);
}
};Since handleOnClick returns a function, we don't need the arrow function anymore. This time, we can access the event object as well as get rid of the arrow function.
Another famous solution is to use the data-* attribute. You can read more about it here Let’s see how we can handle it using data-* attribute
export default function App() {
const handleOnClick = (event) => {
const data = event.currentTarget.dataset.id;
console.log("data", data);
};
return (
<div className="App">
<button onClick={handleOnClick} data-id="5">
Click me
</button>
</div>
);
}export default function App() {
const handleOnClick = (event) => {
const data = event.currentTarget.dataset.id;
console.log("data", data);
};
return (
<div className="App">
<button onClick={handleOnClick} data-id="5">
Click me
</button>
</div>
);
}When you click on the button, you’ll see on the console log:
data 5data 5The downside of this approach is that data-* attribute only accepts string attributes, so in case your data type is not a string, i.e an object, or array, you’ll need to use JSON.stringify to transform them into the string. But pay attention to it because using JSON.stringify is also dangerous (I mentioned in this post)
But most of the time I think the second solution is acceptable.
It’s some notes about event handling in React which I think might be useful in somecase. See you guys next time. Bye bye.