Topic 6: Events
📖 8 min read · 🎯 intermediate · 🧭 Prerequisites: state-and-props, basics-stylescomponents-text-inputs-buttons-scrollview-activity-indicator-images-modals
Why this matters
Picture this: you've built a button in React. It looks great. But when you click it — nothing happens. That's the gap we're closing today. In React, events are how your app listens and responds — to clicks, keystrokes, form submissions, hover actions. Without events, your UI is just a static image. With events, it becomes something users can actually talk to. By the end of this lesson, that humble button you built? It'll do exactly what you tell it to do.
What You'll Learn
- How React's synthetic event system wraps native DOM events for cross-browser consistency
- How to handle click, form submission, and input change events
- How to capture keyboard key presses in real time
- How to respond to mouse hover and focus/blur interactions
The Analogy
Think of React's event system as a city's 911 dispatch center. Citizens (users) send signals — a button tap is a fire alarm, a form submit is a building permit request, a key press is a noise complaint. The dispatch center (React's synthetic event layer) intercepts every incoming call, normalizes it into a standard format no matter which borough it came from, and then routes it to the right handler function on duty. Without dispatch, every precinct would speak its own dialect and chaos would reign. With it, your components just answer the phone and do their job.
Chapter 1: Introduction to React Events
React events look like HTML events but work differently under the hood. React wraps every native browser event into a SyntheticEvent — a cross-browser wrapper that provides a consistent interface regardless of which browser the user is running.
Key difference from plain HTML:
- HTML uses lowercase string handlers:
onclick="handleClick()" - React uses camelCase prop handlers that accept a function reference:
onClick={handleClick}
<button onClick={handleClick}>Click me</button>
You pass the function itself — not a string, not a call. React invokes it when the event fires, passing the SyntheticEvent object as the first argument.
Synthetic events are pooled — React reuses event objects for performance. If you need to access event properties asynchronously, call event.persist() first (or simply destructure the values you need immediately).
Chapter 2: Handling Click Events
The most common event in any UI is a click. the trainer walked the class through a standalone ClickButton component.
src/components/ClickButton.js:
import React from 'react';
function ClickButton() {
const handleClick = () => {
alert('Button was clicked!');
};
return (
<button onClick={handleClick}>
Click me
</button>
);
}
export default ClickButton;
The handler is defined inside the component as an arrow function, then passed by reference to onClick. No parentheses — handleClick, not handleClick() — because you want React to call it later, not immediately during render.
Integrating ClickButton into the App
src/App.js:
import React from 'react';
import './App.css';
import ClickButton from './components/ClickButton';
function App() {
return (
<div className="App">
<h1>React Events Example</h1>
<ClickButton />
</div>
);
}
export default App;
Chapter 3: Handling Form Events
Forms bring two events into play: onChange (fires on every keystroke to sync the input's value into state) and onSubmit (fires when the form is submitted). The critical detail is event.preventDefault() — without it, the browser performs a full page reload on submit.
src/components/SubmitForm.js:
import React, { useState } from 'react';
function SubmitForm() {
const [inputValue, setInputValue] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
alert(`Form submitted with value: ${inputValue}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
}
export default SubmitForm;
e.target.value is how you read what the user typed. Storing it in state with setInputValue makes the input controlled — React owns the value, not the DOM.
Integrating SubmitForm
src/App.js:
import React from 'react';
import './App.css';
import ClickButton from './components/ClickButton';
import SubmitForm from './components/SubmitForm';
function App() {
return (
<div className="App">
<h1>React Events Example</h1>
<ClickButton />
<SubmitForm />
</div>
);
}
export default App;
Chapter 4: Handling Keyboard Events
Keyboard events let you respond to individual key presses — useful for shortcuts, search-as-you-type, or game controls. onKeyPress fires after a key is pressed and a character is produced. The event object exposes event.key (the human-readable key name like "a", "Enter", "ArrowUp").
src/components/KeyPress.js:
import React, { useState } from 'react';
function KeyPress() {
const [key, setKey] = useState('');
const handleKeyPress = (event) => {
setKey(event.key);
};
return (
<div>
<input type="text" onKeyPress={handleKeyPress} placeholder="Press a key" />
<p>Key pressed: {key}</p>
</div>
);
}
export default KeyPress;
Every time the user presses a key inside the input, handleKeyPress fires, reads event.key, and stores it in state — which triggers a re-render that displays the latest key.
Integrating KeyPress
src/App.js:
import React from 'react';
import './App.css';
import ClickButton from './components/ClickButton';
import SubmitForm from './components/SubmitForm';
import KeyPress from './components/KeyPress';
function App() {
return (
<div className="App">
<h1>React Events Example</h1>
<ClickButton />
<SubmitForm />
<KeyPress />
</div>
);
}
export default App;
Chapter 5: Handling Mouse Events
Mouse events track the pointer's relationship with an element. onMouseOver fires when the pointer enters the element's bounds; onMouseOut fires when it leaves. Together they power hover effects without a single line of CSS :hover.
src/components/MouseOver.js:
import React, { useState } from 'react';
function MouseOver() {
const [hovered, setHovered] = useState(false);
const handleMouseOver = () => {
setHovered(true);
};
const handleMouseOut = () => {
setHovered(false);
};
return (
<div>
<div
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
style={{ width: '200px', height: '200px', backgroundColor: hovered ? 'lightblue' : 'lightgray' }}
>
Hover over me
</div>
{hovered && <p>The box is hovered!</p>}
</div>
);
}
export default MouseOver;
The inline style switches backgroundColor based on the hovered boolean in state. The conditional render {hovered && <p>...</p>} shows or hides a message — a pattern you'll use constantly in React UIs.
Integrating MouseOver
src/App.js:
import React from 'react';
import './App.css';
import ClickButton from './components/ClickButton';
import SubmitForm from './components/SubmitForm';
import KeyPress from './components/KeyPress';
import MouseOver from './components/MouseOver';
function App() {
return (
<div className="App">
<h1>React Events Example</h1>
<ClickButton />
<SubmitForm />
<KeyPress />
<MouseOver />
</div>
);
}
export default App;
Chapter 6: Handling Focus Events
Focus events fire when an element gains or loses keyboard focus. onFocus fires when the user clicks into (or tabs to) an input; onBlur fires when focus moves away. These are essential for form validation feedback and accessible UI patterns.
src/components/FocusBlur.js:
import React, { useState } from 'react';
function FocusBlur() {
const [focused, setFocused] = useState(false);
const handleFocus = () => {
setFocused(true);
};
const handleBlur = () => {
setFocused(false);
};
return (
<div>
<input
type="text"
onFocus={handleFocus}
onBlur={handleBlur}
placeholder="Focus on me"
/>
{focused && <p>The input is focused!</p>}
</div>
);
}
export default FocusBlur;
Integrating FocusBlur
src/App.js (final, with all five components wired together):
import React from 'react';
import './App.css';
import ClickButton from './components/ClickButton';
import SubmitForm from './components/SubmitForm';
import KeyPress from './components/KeyPress';
import MouseOver from './components/MouseOver';
import FocusBlur from './components/FocusBlur';
function App() {
return (
<div className="App">
<h1>React Events Example</h1>
<ClickButton />
<SubmitForm />
<KeyPress />
<MouseOver />
<FocusBlur />
</div>
);
}
export default App;
Chapter 7: Putting It Together
flowchart LR
User([User Action]) --> SyntheticEvent[React SyntheticEvent]
SyntheticEvent --> onClick
SyntheticEvent --> onSubmit
SyntheticEvent --> onKeyPress
SyntheticEvent --> onMouseOver
SyntheticEvent --> onMouseOut
SyntheticEvent --> onFocus
SyntheticEvent --> onBlur
onClick --> HandlerFn[Handler Function]
onSubmit --> HandlerFn
onKeyPress --> HandlerFn
onMouseOver --> HandlerFn
onMouseOut --> HandlerFn
onFocus --> HandlerFn
onBlur --> HandlerFn
HandlerFn --> setState[setState / Update]
setState --> ReRender[Re-render UI]
Every interaction follows the same pipeline: user action → synthetic event → handler function → state update → re-render. Once you understand the loop, every new event type is just a new entry point into the same flow.
🧪 Try It Yourself
Task: Build a PasswordStrength component that shows a strength label as the user types.
Success criterion: As you type in the input, you should see one of three messages appear below it — "Weak" (fewer than 6 characters), "Medium" (6–10 characters), or "Strong" (more than 10 characters).
Starter snippet:
import React, { useState } from 'react';
function PasswordStrength() {
const [password, setPassword] = useState('');
const getStrength = () => {
if (password.length === 0) return '';
if (password.length < 6) return 'Weak';
if (password.length <= 10) return 'Medium';
return 'Strong';
};
return (
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter a password"
/>
<p>Strength: {getStrength()}</p>
</div>
);
}
export default PasswordStrength;
Drop this into src/components/PasswordStrength.js, import it in App.js, and watch the label update with every keystroke.
🔍 Checkpoint Quiz
Q1. Why does React use SyntheticEvents instead of native DOM events?
A) To make event handling slower but more predictable
B) To provide a consistent cross-browser interface that normalizes event behavior
C) Because native DOM events don't support onClick
D) To prevent events from bubbling up the component tree
Q2. Given this component, what happens when you click the button?
function Boom() {
const fire = () => alert('Boom!');
return <button onClick={fire()}>Launch</button>;
}
A) An alert saying "Boom!" appears when the button is clicked
B) An alert saying "Boom!" appears immediately when the component renders
C) Nothing happens — onClick doesn't accept return values
D) A TypeError is thrown because fire() returns undefined
Q3. What does event.preventDefault() do inside an onSubmit handler?
A) Prevents the onChange event from firing
B) Stops the browser's default form behavior (a full page reload/GET request)
C) Prevents any child elements from receiving the event
D) Disables the submit button after one click
Q4. How would you use onBlur to show a validation error only after the user leaves an input field (not while they're typing)?
A) Add onChange with a timeout
B) Set an error state to true inside the onBlur handler, and render the error message conditionally based on that state
C) Use onKeyPress and check if event.key === 'Tab'
D) Pass validate={true} as a prop to the <input>
A1. B — SyntheticEvents normalize browser differences so the same handler code works in Chrome, Firefox, Safari, and Edge without special cases.
A2. B — onClick={fire()} calls fire immediately during render (because of the ()), so the alert pops before the user ever clicks anything. It should be onClick={fire}.
A3. B — Without event.preventDefault(), submitting a form triggers the browser's native submission, which reloads the page and wipes your React state.
A4. B — Store a boolean like touched or hasError in state, set it to true inside onBlur, and conditionally render {hasError && <span>Required</span>}. This gives users a chance to finish typing before seeing errors.
🪞 Recap
- React wraps native browser events into SyntheticEvents for a consistent, cross-browser API.
- Pass event handlers as function references —
onClick={handler}, neveronClick={handler()}. event.preventDefault()inonSubmitstops the browser from reloading the page on form submission.event.keyinsideonKeyPresstells you exactly which key was pressed.onMouseOver/onMouseOutandonFocus/onBlurlet you drive hover and focus states through React's own state system.
📚 Further Reading
- React Docs — Handling Events — the official guide with interactive examples
- SyntheticEvent Reference — full list of supported events and properties
- MDN — Event reference — the underlying native events React wraps
- ⬅️ Previous: Basics: Styles, Components, Text Inputs, Buttons, ScrollView, Activity Indicator, Images, Modals
- ➡️ Next: File System