In this tutorial, we’ll go over the basics of React’s onClick
event handler, including event listening, binding methods in class components, and dealing with custom events.
What are event handlers in React?
Event handlers determine what action is to be taken whenever an event is fired. This could be a button click or a change in a text input.
Essentially, event handlers are what make it possible for users to interact with your React app.
Handling events with React elements is similar to handling events on DOM elements, with a few minor exceptions. If you’re familiar with how events work in standard HTML and JavaScript, it should be easy for you to learn how to handle events in React.
If you’re new to React or just need a quick refresher on handling events, this video tutorial breaks down the concept quite nicely.
What is the onClick
handler in React?
The React onClick
event handler enables you to call a function and trigger an action when a user clicks an element, such as a button, in your app.
Event names are written in camelCase, so the onclick
event is written as onClick
in a React app. In addition, React event handlers appear inside curly braces.
Take the following simple example written in HTML:
<button onclick="sayHello()"> Say Hello <button>
In a React app, this would be written as follows:
<button onClick={sayHello}> Say Hello <button>
Another key difference is that, whereas you would simply return false
to avoid default behavior in HTML, in React, you must explicitly call preventDefault
.
The following example shows how to prevent a link from opening a new page by default:
<a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a>
You would write this as follows in React:
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ); }
Synthetic events in React
React implements a synthetic events system that brings consistency and high performance to React apps and interfaces. It achieves consistency by normalizing events so that they have the same properties across different browsers and platforms.
A synthetic event is a cross-browser wrapper around the browser’s native event. It has the same interface as the browser’s native event, including stopPropagation()
and preventDefault()
, except the events work identically across all browsers.
It achieves high performance by automatically using event delegation. In actuality, React doesn’t attach event handlers to the nodes themselves. Instead, a single event listener is attached to the root of the document. When an event is fired, React maps it to the appropriate component element.
Listening to events in React
To listen to events in React, add the onClick
attribute, which is the event handler, to the target element. This specifies the function to be executed when that element is clicked.
import React, { Component } from "react"; import ReactDOM from "react-dom"; class ShowAlert extends Component { showAlert() { alert("I'm an alert"); } render() { return <button onClick={this.showAlert}>show alert</button>; } } export default ShowAlert;
In the example above, the onClick
attribute is set to the showAlert
function, which alerts a message. This means that whenever the button is clicked, the showAlert
function is called, which, in turn, shows the alert box.
Handling events in class components
In JavaScript, class methods are not bound by default. Therefore, it’s necessary to bind functions to the class instance.
Binding in the render()
method
One way to resolve the problem of binding is to call bind in a render
function.
In the example above, we are using the onChange
event handler to listen for typing events on a text input. This is done by binding it in the render()
function. This method requires calling .bind(this)
in the render()
function.
import React, { Component } from "react"; import ReactDOM from "react-dom"; class ChangeInput extends Component { constructor(props) { super(props); this.state = { name: "" }; } changeText(event) { this.setState({ name: event.target.value }); } render() { return ( <div> <label htmlFor="name">Enter Text here </label> <input type="text" id="name" onChange={this.changeText.bind(this)} /> <h3>{this.state.name}</h3> </div> ); } } export default ChangeInput;
This is because any ES6 class methods are plain JavaScript functions; therefore, it inherits bind()
from the Function
method. So now when we call onChange()
inside JSX, this
will point to our class instance. Easy-peasy.
However, using this method means that there might be some performance implications since the function is reallocated on every render. This performance cost might not be visible at all in small React apps, but it could become noticeable in bigger React apps.
Binding in the constructor()
method
If binding in the render doesn’t work for you, you can bind in the constructor. See an example below:
import React, { Component } from "react"; import ReactDOM from "react-dom"; class ChangeInput extends Component { constructor(props) { super(props); this.state = { name: "" }; this.changeText = this.changeText.bind(this); } changeText(event) { this.setState({ name: event.target.value }); } render() { return ( <div> <label htmlFor="name">Enter Text here </label> <input type="text" id="name" onChange={this.changeText} /> <h3>{this.state.name}</h3> </div> ); } } export default ChangeInput;
As you can see above, the changeText
function is bound in the constructor
method.
this.changeText = this.changeText.bind(this)
Let’s go over how the line of code above works.
The first this.changeText
refers to the changeText
method. Since this is done in the constructor, this
refers to the ChangeInput
class component.
The second this.changeText
is also referring to the same changeText()
method, but we are now calling .bind()
on it.
The final this
is the context we are passing to .bind()
, and it refers to the ChangeInput
class component.
It’s also important to note that if changeText
isn’t bound to the class instance, it won’t be able to access this.setState
because this
will be undefined
. This is another important reason to bind event handling functions.
Binding with arrow functions
You can handle events in class components by binding them with the fat arrow function.
ES7 class properties enable bindings at the method definition, as shown in the example below. By definition, an arrow function expression has a shorter syntax than a function expression and does not have its own this
, arguments
, super
, or new.target
.
import React, { Component } from "react"; import ReactDOM from "react-dom"; class ChangeInput extends Component { handleEvent = event => { alert("I was clicked"); }; render() { return ( <button onClick={this.handleEvent}>Click on me</button> ); } } export default ChangeInput;
In the example above, once the component has been created, the this.handleEvent
will never change again. That, in turn, means <button>
won’t get rerendered. This approach is very simple and easy to read.
Handling events in functional components
There are several ways to handle events in functional React components. We’ll go over five of them here.
1. Call an inline function in an onClick
event handler
Inline functions allow you to write code for event handling directly in JSX. See the example below:
import React from "react"; const App = () => { return ( <> <button onClick={() => alert("Hello!")}>Say Hello</button> </> ); }; export default App;
This is commonly used to avoid the extra function declaration outside the JSX, although it can be less readable and harder to maintain if the content of the inline function is too much.
2. Update the state inside an onClick
event handler
Let’s say your React application requires you to update the local state in an onClick
event handler. Here’s how to do that:
import React, { useState } from "react"; const App = () => { const [count, setCount] = useState(0); return ( <> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={() => setCount(count - 1)}>Decrement</button> </> ); }; export default App;
In the example above, the value of local state is modified by the Increment
and Decrement
buttons, which have the setCount
, an updater function inside the onClick
event handler.
3. Call multiple functions in an onClick
event handler
The onClick
event handler also allows you to call multiple functions.
import React, { useState } from "react"; const App = () => { const [count, setCount] = useState(0); const sayHello = () => { alert("Hello!"); }; return ( <> <p>{count}</p> <button onClick={() => { sayHello(); setCount(count + 1); }} > Say Hello and Increment </button> </> ); }; export default App;
In the code block above, clicking the button increments the local state and alerts a message. Both actions are executed by separate functions in the onClick
event handler.
4. Pass a parameter to an onClick
event handler
Another common use case for event handlers is passing a parameter to a function so it can be used later. For example:
import React from "react"; const App = () => { const sayHello = (name) => { alert(`Hello, ${name}!`); }; return ( <button onClick={() => { sayHello("Yomi"); }} > Say Hello </button> ); }; export default App;
Here, the sayHello
function accepts a name as a parameter, which is then used to alert a message.
5. Use synthetic events directly inside an onClick
event handler
You can also use synthetic events directly inside an onClick
event handler. In the example below, the button’s value is gotten via e.target.value
and then used to alert a message.
import React from "react"; const App = () => { return ( <button value="Hello!" onClick={(e) => alert(e.target.value)}> Say Hello </button> ); }; export default App;
Custom components and events in React
When it comes to events in React, only DOM elements are allowed to have event handlers. Take the example of a component called CustomButton
with an onClick
event. This wouldn’t respond to clicks because of the reason above.
So how do we handle event handling for custom components?
By rendering a DOM element inside the CustomButton
component and passing the onClick
prop into it. Our CustomButton
is essentially a pass-through for the click event.
import React from "react"; const CustomButton = ({ onPress }) => { return ( <button type="button" onClick={onPress}> Click on me </button> ); }; const App = () => { const handleEvent = () => { alert("I was clicked"); }; return <CustomButton onPress={handleEvent} />; }; export default App;
In the example above, the CustomButton
component is passed a prop of onPress
, which then gets passed into the onClick
of the button
.
Conclusion
Event handlers determine what action should be taken when an event occurs. The onClick
event is used to listen for click events on DOM elements.
When it comes to event handling in class components, binding is important, and there are a couple ways to go about it, which we covered in this tutorial.
We also reviewed some common use cases of the onClick
event handler in functional components, such as updating the state, calling multiple functions, and using synthetic events.
Lastly, we addressed how the onClick
event handler works in custom components.
Monitor all onClick
events in production
Debugging React applications can be difficult, especially when there is complex state. If you’re interested in monitoring and tracking Redux state for all of your users in production, try LogRocket.
LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps — start monitoring for free.
Thanks brother. This is simple and adequate. It cleared all queries in my head.
The code is to be changed. It should have been:
“`js
this.changeText = this.changeText.bind(this)
“`
This is fixed – thank you for bringing this to our attention.