Handling Events
React events look like HTML events but are camelCased and take a function reference instead of a string. React uses synthetic events — a cross-browser wrapper around native browser events.
Basic Event Handlers
basic.jsx
import { useState } from 'react';
function ClickCounter() {
const [count, setCount] = useState(0);
// Define handler as a separate function
function handleClick() {
setCount(count + 1);
console.log('Button clicked!');
}
return (
<div>
<p>Clicked {count} times</p>
{/* Pass function reference, not a call */}
<button onClick={handleClick}>Click me</button>
{/* Or use inline arrow function */}
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
The Event Object
Every event handler receives a synthetic event object with useful properties:
event-object.jsx
function InputLogger() {
function handleChange(e) {
console.log(e.target.value); // current input value
console.log(e.target.name); // input's name attribute
console.log(e.type); // "change"
console.log(e.target.checked); // for checkboxes
}
function handleKeyDown(e) {
if (e.key === 'Enter') {
console.log('Enter pressed!');
}
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
console.log('Ctrl+S intercepted');
}
}
return (
<input
name="search"
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder="Type something..."
/>
);
}
Passing Arguments
To pass extra arguments to an event handler, wrap it in an arrow function:
arguments.jsx
function TodoList() {
const todos = ['Buy milk', 'Write code', 'Sleep'];
function handleDelete(index) {
console.log(`Deleting todo at index ${index}`);
}
function handleEdit(todo, index) {
console.log(`Editing "${todo}" at index ${index}`);
}
return (
<ul>
{todos.map((todo, i) => (
<li key={i}>
{todo}
{/* Wrap in arrow function to pass arguments */}
<button onClick={() => handleDelete(i)}>Delete</button>
<button onClick={() => handleEdit(todo, i)}>Edit</button>
</li>
))}
</ul>
);
}
Common Event Types
event-types.jsx
function EventExamples() {
return (
<div>
{/* Mouse events */}
<button onClick={() => console.log('clicked')}>Click</button>
<button onDoubleClick={() => console.log('double!')}>Double-click</button>
<div onMouseEnter={() => console.log('hover in')}
onMouseLeave={() => console.log('hover out')}>
Hover me
</div>
{/* Keyboard events */}
<input onKeyDown={e => console.log('key down:', e.key)}
onKeyUp={e => console.log('key up:', e.key)} />
{/* Form events */}
<input onChange={e => console.log('value:', e.target.value)} />
<select onChange={e => console.log('selected:', e.target.value)}>
<option value="a">Option A</option>
<option value="b">Option B</option>
</select>
{/* Focus events */}
<input onFocus={() => console.log('focused')}
onBlur={() => console.log('blurred')} />
</div>
);
}
Preventing Default Behavior
For links and forms, call e.preventDefault() to stop the browser's default action:
prevent-default.jsx
function LoginForm() {
function handleSubmit(e) {
e.preventDefault(); // Stops page reload
const form = e.target;
const email = form.email.value;
const password = form.password.value;
console.log('Logging in:', email);
// Now call your API...
}
function handleLinkClick(e) {
e.preventDefault(); // Stops navigation
console.log('Link clicked but page did not navigate');
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
<button type="submit">Login</button>
<a href="/signup" onClick={handleLinkClick}>Sign up</a>
</form>
);
}