- Understand how to use React events in our application
- Attach event listeners to JSX elements
In this lesson, we'll cover the event system in React. We'll learn how to attach event listeners to specific elements.
Consider the following component:
function Tickler() {
function tickle() {
console.log("Teehee!");
}
return <button>Tickle me!</button>;
}
We have a tickle()
function, but no way to trigger it! This is a perfect time
to add an event listener so that we can see the message in our console. We attach
an event listener to an element much like we add a prop.
The listener name is always comprised of on
, and the event name itself, for
example click
. These are joined together and camel-cased, so if we wanted to
add a click handler, we'd call the prop onClick
.
This prop takes a callback function as a value. It can either be a reference
to a function (like our tickle()
function), or an inline function. Most of the
time, we'll use a function reference.
All together, it looks like this:
<button onClick={tickle}>Tickle me!</button>
As you can see, we're passing a function reference, and not executing the
tickle
function. Our updated component looks like this:
function Tickler() {
function tickle() {
console.log("Teehee!");
}
return <button onClick={tickle}>Tickle me!</button>;
}
Now, when we click the button, we see a message in our console. Awesome!
We can also use an arrow function to define an event handler inline, and pass it to the event listener:
function Tickler() {
return <button onClick={() => console.log("Teehee!")}>Tickle me!</button>;
}
Arrow functions are a good choice if your event handler doesn't need to handle much logic. If you have more than one line of code to run in your event handler, it's a good idea to create a separate callback function (like in the first example).
One important thing to note about event listeners: you can only attach them to DOM elements, not React components. For example, this will not work:
function Clickable() {
return <button>Click Me</button>;
}
function App() {
function handleClick() {
console.log("click");
}
return <Clickable onClick={handleClick} />;
}
... but this will:
function Clickable() {
function handleClick() {
console.log("click");
}
return <button onClick={handleClick}>Click Me</button>;
}
function App() {
return <Clickable />;
}
If we want to make the first example work so that handleClick
is called in
App
, we'd have to pass onClick
as a prop on the Clickable
component,
like so:
function Clickable({ onClick }) {
return <button onClick={onClick}>Click Me</button>;
}
function App() {
function handleClick() {
console.log("click");
}
return <Clickable onClick={handleClick} />;
}
In this example, we're passing down a reference to the handleClick
function as
a prop called onClick
to the Clickable
component. Then, we're using that
prop as the callback function for the <button>
element's onClick
attribute.
That way, when the <button>
element is clicked, the callback function
handleClick
will be called.
Whew! That's a lot to keep track of. We'll cover this concept of passing down callback functions as props in more detail later on.
Let's explore a few other common event types and their use cases here. There's some starter code provided, so feel free to code along and test things out in the console!
As we saw in the example above, adding a click
event is pretty straightforward!
Using our Tickler
component as an example, let's see what else we can do with
a click event.
Update your component to look like this:
function Tickler() {
function tickle(event) {
console.log(event);
}
return <button onClick={tickle}>Tickle me!</button>;
}
Just as in JavaScript, when we handle events in React, we can provide an event
parameter to our event handler callback function. When the button is clicked, we
can access all the information about the event (the event target
, mouse
coordinates via clientX
and clientY
, etc).
What if we wanted to pass other information to the event handler though? In the
MultiButton
component, we have three buttons that all share the same callback
function for their onClick
event:
function MultiButton() {
function handleClick(number) {
console.log(`Button ${number} was clicked`);
}
return (
<div>
<button onClick={handleClick}>Button 1</button>
<button onClick={handleClick}>Button 2</button>
<button onClick={handleClick}>Button 3</button>
</div>
);
}
When one of the buttons is clicked, we want the callback to log out the button's
number. If you try clicking one of those buttons now, you'll still see the
event
object being logged, not the number of the button.
We could try this:
function MultiButton() {
function handleClick(number) {
console.log(`Button ${number} was clicked`);
}
return (
<div>
<button onClick={handleClick(1)}>Button 1</button>
<button onClick={handleClick(2)}>Button 2</button>
<button onClick={handleClick(3)}>Button 3</button>
</div>
);
}
...but now, the console messages will appear as soon as our component is rendered, not when the button is clicked. This is why we always need to provide a function definition, not a function invocation to our event handlers. Here's the solution:
function MultiButton() {
function handleClick(number) {
console.log(`Button ${number} was clicked`);
}
return (
<div>
<button onClick={() => handleClick(1)}>Button 1</button>
<button onClick={() => handleClick(2)}>Button 2</button>
<button onClick={() => handleClick(3)}>Button 3</button>
</div>
);
}
By writing out an arrow function here, we're providing each of our button's
onClick
handlers a function definition that will only be invoked when the
button is clicked.
The onChange
attribute is useful for handling changes to input values. This
event listener is often used with <input>
, <select>
, and <textarea>
inputs
(basically, anywhere you need to capture a user's input).
Here's an example of using the onChange
handler:
function ChangeItUp() {
function handleChange(event) {
console.log(`${event.target.name}: ${event.target.value}`);
}
return (
<div>
<input
type="text"
name="search"
onChange={handleChange}
placeholder="Enter search term..."
/>
<select name="filter" onChange={handleChange}>
<option value="all">Select a filter...</option>
<option value="completed">Completed</option>
<option value="incomplete">Incomplete</option>
</select>
</div>
);
}
Try it out and note that, as text is entered into the <input>
field, its value
is captured using event.target.value
and logged to the console.
Whenever you're working with <form>
elements, handling the submit event is a
good way to interact with all the data from the form after it's been submitted.
Here's a quick example:
function Login() {
function handleSubmit(event) {
event.preventDefault();
console.log("I submit");
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="username" placeholder="Enter username..." />
<input type="password" name="password" placeholder="Enter password..." />
<button>Login</button>
</form>
);
}
When the form is submitted, just like in vanilla JavaScript, you must call
event.preventDefault()
to prevent the form from making a network request.
We'll go into forms in more detail in a later lesson, and show the preferred way
for collecting data from all the form input fields. For now, just remember:
use the onSubmit
event handler, and always call preventDefault()
!
You may have noticed when inspecting the event
object that it's a bit
different than the standard browser event. React's event
object is a special
object called: SyntheticBaseEvent
.
React has its own event system with special event handlers called
SyntheticEvent
. The reason for having a specific event system instead of using
native events is cross-browser compatibility. Some browsers treat events
differently, and by wrapping these events into a consistent API, React makes our
lives a lot easier. It's important to keep in mind that they are the exact same
events, just implemented in a consistent way! That means these events also have
methods that you can call like preventDefault()
, stopPropagation()
, and so
on.
In React, you can add event listeners to elements in JSX by providing an
onEvent
attribute and passing a callback function to be used as an event
handler. Some commonly used event listeners include onClick
, onChange
, and
onSubmit
. You can find a full list of supported events
here.
React has its own internal events system that makes events behave consistently across various browsers.