
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:






