How to Work With Event Listeners in React
This lesson is a preview from our interactive course
In this lesson, we are going to dive deeper into how event listeners and handlers work in React, and which events are the most commonly used. Each event listener in React starts with the "on" prefix. For example, onClick
, onChange
, or onKeyUp
.
onClick#
As a quick recap, here is an example of how we could handle the most common event: click events.
import React, { useState } from 'react'
const App = () => {
const [count, setCount] = useState(0);
return (
<React.Fragment>
<h1>The current count is: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</React.Fragment>
);
}
export default App;
Note the difference between event listeners and handlers. onClick
is the event listener, while the function passed to it is the event handler.
onClick
event listeners can be attached to pretty much any element where a click can occur. However, keep in mind that for semantics, in most cases you want to use interactive elements, such as a button or an anchor.
onChange#
Another commonly used event in React is the onChange
event. onChange
events are mostly used with inputs where the event handler will be triggered on each change event. Take the following example:
import React, { useState } from 'react'
const App = () => {
const [value, setValue] = useState('');
return (
<React.Fragment>
<h1>Input value: "{value}"</h1>
<input onChange={event => setValue(event.target.value)} />
</React.Fragment>
);
}
export default App;
We have access to the event itself inside the event handler. We can use event.target.value
to grab the current value of the input
and output it into the h1
. Each time the input changes, the event handler gets called. Try to recreate the above example using a select
element.
Instead of using the selected
attribute on one of the options, we need to use the defaultValue
prop on the select
element for default values.
Notice that we need to change the initial value of the useState
hook to reflect the default selected option. Otherwise, the default value only gets applied if we change to another option and change back to the default value. The same onChange
event listener is used for checkboxes:
import React, { useState } from 'react'
const App = () => {
const [value, setValue] = useState(false);
return (
<React.Fragment>
<h1>Input checked: "{JSON.stringify(value)}"</h1>
<input
type="checkbox"
onChange={event => setValue(event.target.checked)}
/>
</React.Fragment>
);
}
export default App;
In such cases, we need to look for the event.target.checked
value, which tells us whether the checkbox is checked or not. As a result, the value
state needs to be a boolean.
But what if we want this to be checked by default? In this case, we also need to pass the value
state to the checked
property on the input:
import React, { useState } from 'react'
export const App = () => {
const [value, setValue] = useState(true);
return (
<React.Fragment>
<h1>Input checked: "{JSON.stringify(value)}"</h1>
<input
type="checkbox"
onChange={event => setValue(event.target.checked)}
checked={value}
/>
</React.Fragment>
);
}
export default App;
Passing Event Handlers#
So far we only looked at inline event handlers. The most common way to use these functions in React, however, is to define them above the return
statement, and pass them to one of the event listeners. Taking our onClick
as an example, it would translate to the following:
import React, { useState } from 'react'
const App = () => {
const [count, setCount] = useState(0);
// The event object is also available inside the function
const updateCount = event => setCount(count + 1);
return (
<React.Fragment>
<h1>The current count is: {count}</h1>
<button onClick={updateCount}>
Increase
</button>
</React.Fragment>
);
}
export default App;
Notice that the function is only passed to the onClick
event listener, but not called. Calling the function inside the event listener would call the function as soon as the component is rendered.
// β Don't call the function during render
<button onClick={updateCount()}>Increase</button>
// βοΈ Do call it with an inline handler
<button onClick={event => updateCount(event, param)}>Increase</button>
// β
Prefer passing the function
<button onClick={updateCount}>Increase</button>
Inline event handlers can also be used when we need to pass extra parameters to the function. Note that, for the third example, the event
object is automatically passed, so we don't need to pass it explicitly.
Event handlers as props#
Event handlers can also be passed as props. This is useful in case we want to handle events for a component outside of its scope. For example, we might have a generic Button
component that needs to handle different events for different buttons. We can achieve this in the following way:
import React, { useState } from 'react'
const Button = ({ onClick, children }) => {
return <button onClick={onClick}>{children}</button>;
}
const App = () => {
const [value, setValue] = useState('');
return (
<React.Fragment>
<h1>Button clicked: {value}</h1>
<Button onClick={() => setValue('first')}>First</Button>
<Button onClick={() => setValue('last')}>Last</Button>
</React.Fragment>
);
}
export default App;
Notice that anything passed between the component will be accessible with a variable called children
.
Event Propagation#
There is one more thing we need to cover in this lesson. How to prevent event propagation and default browser actions.
Event propagation happens when there are multiple event listeners attached to one of the elements and its descendants. When the event is triggered, it starts to bubble up the DOM, starting from the innermost element. Take the following as an example:
import React from 'react'
const App = () => {
const divClick = () => console.log('clicked on div');
const buttonClick = () => console.log('clicked on button');
return (
<div onClick={divClick}>
<button onClick={buttonClick}>
Trigger
</button>
</div>
);
}
export default App;
We have two different onClick
event listeners attached to a div
and a button
inside of it. Execute the code and click on the button to verify that both logs are logged to the console.
This behavior could be unwanted in certain cases. To prevent it, we need to stop event propagation using the stopPropagation
method on the event
object:
import React from 'react'
const App = () => {
const divClick = () => console.log('clicked on div');
const buttonClick = e => {
e.stopPropagation();
console.log('clicked on button');
}
return (
<div onClick={divClick}>
<button onClick={buttonClick}>
Trigger
</button>
</div>
);
}
export default App;
The event
object is often abbreviated to e
.
Execute the code again, and click on the button. This time, only the onClick
attached to the button will be triggered.
Preventing Default Actions#
Event propagation is not the only thing that can alter the behavior of our event listeners. Some events also come with default browser actions. A classic example is the onSubmit
event for a form
.
By default, browsers refresh the page whenever the submit event is triggered. For single-page applications, this usually doesn't make much sense as we want to handle user events on the client for a better experience. Take the following as an example:
import React from 'react'
const App = () => {
const submit = () => {
console.log('Submitting form...');
}
return (
<form onSubmit={submit}>
<button>Submit</button>
</form>
);
}
export default App;
In this example, the page gets reloaded when the form is submitted. To prevent this, try to modify the submit
function by adding e.preventDefault
above the console.log
.
Don't forget to also pass the event
as the parameter to the function. You can also use e.preventDefault
and e.stopPropagation
together when both actions need to be suppressed.
Now that we have a good understanding of how we can respond to user events in React, there is only one more thing we need to cover before the next section. That is how to achieve routing.
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: