Events in React Masterclass
A masterclass tutorial covering Click Events, Change Events, Submit Events, Keyboard Events, Mouse Events, Event Handlers, Passing Arguments, Synthetic Events, preventDefault(), Event Bubbling & stopPropagation(), Pitfalls, and Interview Questions.
What are Events in React?
Web applications are driven by user actions: clicking buttons, typing in text boxes, hovering over images, submitting forms, or pressing keyboard shortcuts.
In React, handling events is syntactically similar to handling events on DOM elements, with a few key differences in naming, binding, and event delegation.
Key Differences Between HTML Events and React Events
| Feature | HTML DOM Events | React Synthetic Events |
|---|---|---|
| Naming Convention | Lowercase (onclick, onchange) | CamelCase (onClick, onChange) |
| Handler Type | String: onclick="handleClick()" | Function Reference: onClick={handleClick} |
| Default Prevention | Return false | Must explicitly call e.preventDefault() |
| Event Delegation | Direct node binding | Automatic root delegation (React Root element) |
SyntheticEvent System in React
When an event fires in React, React passes a SyntheticEvent object wrapper to your handler function rather than a raw browser native event.
Why SyntheticEvent?
- Cross-Browser Consistency: Normalizes browser inconsistencies between Chrome, Firefox, Safari, and Edge.
- High Performance: Uses Event Delegation at the root root container node to conserve system memory.
Common Event Types in React
- Click Events:
onClick,onDoubleClick - Change Events:
onChange(Fires on input, select, textarea value modification) - Form Submit Events:
onSubmit(Fires when form is submitted via Enter key or Submit button) - Keyboard Events:
onKeyDown,onKeyUp,onKeyPress - Mouse Events:
onMouseEnter,onMouseLeave,onMouseMove - Focus Events:
onFocus,onBlur
Passing Arguments to Event Handlers
If your handler needs additional custom arguments (e.g. passing a product ID), wrap the function invocation inside an arrow function:
// โ
CORRECT: Arrow function wrapper
<button onClick={() => handleDeleteProduct(product.id)}>Delete Item</button>
// โ WRONG: Invokes function immediately during rendering!
<button onClick={handleDeleteProduct(product.id)}>Delete Item</button>Form Submission & preventDefault()
By default, HTML forms reload the browser page when submitted. In Single Page Applications (SPAs), we must call e.preventDefault() to cancel page reloads and process inputs in JavaScript:
function ContactForm() {
const handleSubmit = (e) => {
e.preventDefault(); // Prevents browser page refresh!
console.log("Form submitted via AJAX!");
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Your name" />
<button type="submit">Submit Form</button>
</form>
);
}Event Bubbling & Stopping Propagation (stopPropagation)
In the DOM, events bubble up from child nodes to parent nodes.
If clicking a child button triggers a parent card click handler unexpectedly, use e.stopPropagation():
function Card({ onCardClick, onDelete }) {
return (
<div onClick={onCardClick} className="card">
<h3>Card Title</h3>
<button
onClick={(e) => {
e.stopPropagation(); // Stops click from bubbling up to parent card!
onDelete();
}}
>
Delete Card
</button>
</div>
);
}Common Mistakes with Event Handlers
- Invoking handler immediately:
onClick={handleClick()}instead ofonClick={handleClick}. - Forgetting
preventDefault()on forms: Causes full browser page reload and loss of application state. - Confusing
event.targetandevent.currentTarget:
e.target= The exact element that triggered the event (e.g., inner icon).e.currentTarget= The element that the event listener is attached to.
Top React Interview Questions on Events
Q1: What is a SyntheticEvent in React?
*Answer*: SyntheticEvent is a cross-browser wrapper around the browser's native event object, standardizing properties and handling event delegation at the root root node.
Q2: What is the difference between e.target and e.currentTarget?
*Answer*: e.target points to the target element where the event originated, whereas e.currentTarget points to the element handling the event listener.
Q3: How do you prevent event bubbling in React?
*Answer*: Invoke e.stopPropagation() inside the child event handler.
Practical Hands-On Exercise โ Complete Event Explorer Component
Here is a complete interactive application handling click, input change, form submission, keyboard shortcuts, argument passing, and propagation stopping:
// Complete Masterclass Events Showcase (src/App.jsx)
import React, { useState } from 'react';
export default function App() {
const [inputText, setInputText] = useState("");
const [lastKey, setLastKey] = useState("None");
const [logs, setLogs] = useState([]);
const addLog = (msg) => {
setLogs(prev => [`[${new Date().toLocaleTimeString()}] ${msg}`, ...prev.slice(0, 4)]);
};
const handleFormSubmit = (e) => {
e.preventDefault();
addLog(`Submitted Form with text: "${inputText}"`);
setInputText("");
};
const handleKeyDown = (e) => {
setLastKey(e.key);
if (e.key === 'Enter' && e.ctrlKey) {
addLog("Shortcut Triggered: Ctrl + Enter!");
}
};
return (
<div style={{ maxWidth: "600px", margin: "30px auto", padding: "20px", background: "#f8fafc", borderRadius: "16px" }}>
<h2 style={{ color: "#002057" }}>Lecture 8: React Events Masterclass Explorer</h2>
{/* Interactive Form */}
<form onSubmit={handleFormSubmit} style={{ marginBottom: "20px" }}>
<label style={{ display: "block", marginBottom: "6px", fontWeight: "bold" }}>Type Something & Press Enter:</label>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Try pressing Ctrl + Enter..."
style={{ width: "100%", padding: "10px", borderRadius: "6px", border: "1px solid #cbd5e1", marginBottom: "10px" }}
/>
<button type="submit" style={{ background: "#2506ad", color: "#fff", border: "none", padding: "10px 16px", borderRadius: "6px", fontWeight: "bold" }}>
Submit Form
</button>
</form>
{/* Event Bubbling Demo */}
<div
onClick={() => addLog("Parent Card Clicked!")}
style={{ padding: "20px", background: "#e2e8f0", borderRadius: "10px", marginBottom: "20px", cursor: "pointer" }}
>
<p style={{ margin: 0, fontWeight: "bold" }}>Parent Card Container (Clickable)</p>
<button
onClick={(e) => {
e.stopPropagation(); // Stops propagation!
addLog("Child Button Clicked (Propagation Stopped!)");
}}
style={{ marginTop: "10px", background: "#059669", color: "#fff", border: "none", padding: "8px 12px", borderRadius: "6px" }}
>
Child Button (Stops Propagation)
</button>
</div>
{/* Status Log */}
<div style={{ padding: "12px", background: "#0f172a", color: "#38bdf8", borderRadius: "8px", fontFamily: "monospace" }}>
<div>Last Key Pressed: <strong>{lastKey}</strong></div>
<hr style={{ borderColor: "#334155" }} />
{logs.map((log, idx) => (
<div key={idx}>{log}</div>
))}
</div>
</div>
);
}