onChange keeps the value; onBlur checks the finished edit; onFocus can clear the error when they retry.
Mistake: validate format on every keystroke
// Wrong — "a" is instantly "invalid email"
onChange={(e) => {
setEmail(e.target.value);
setError(e.target.value.includes('@') ? null : 'Invalid');
}}
// Right — update on change, validate on blur
onChange={(e) => setEmail(e.target.value)}
onBlur={() => setError(email.includes('@') ? null : 'Invalid')}
Blur matches “I finished editing this field.”
Forms: onSubmit and preventDefault
Definition
onSubmit runs when a form is submitted (Enter in a field or a submit button). Call event.preventDefault() in SPAs to avoid a full document navigation.
In simpler words
Handle create/update in onSubmit, not only in a button onClick — Enter key users depend on it.
Read drafts from state inside the submit handler, then call Nest (via Query mutation / Axios).
Disable the submit button while pending to prevent double posts.
SPA form submit
function CreateForm({ onCreate }) {
const [title, setTitle] = useState('');
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!title.trim()) return;
await onCreate({ title });
setTitle('');
}
return (
<form onSubmit={handleSubmit}>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
<button type="submit">Create</button>
</form>
);
}
preventDefault keeps the page from reloading; then you own the Nest call.
Mistake: missing preventDefault
// Wrong — full page reload; React state looks "wiped"
function handleSubmit() {
createTicket({ title });
}
// Right
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
createTicket({ title });
}
Always preventDefault in SPA onSubmit handlers.
Keyboard handlers
Definition
Keyboard handlers (onKeyDown, onKeyUp, onKeyPress-legacy) respond to key events for shortcuts, Escape-to-close, and Enter-to-confirm.
In simpler words
Prefer onKeyDown for shortcuts. Check e.key (not deprecated keyCode).
Escape often closes dialogs; Enter may confirm if not inside a textarea.
Do not block browser accessibility shortcuts without a strong reason.
Escape closes a panel
function Dialog({ open, onClose, children }) {
if (!open) return null;
return (
<div
role="dialog"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Escape') onClose();
}}
>
{children}
</div>
);
}
Keyboard paths matter as much as click paths for production UI.
Mistake: deprecated keyCode
// Wrong
if (e.keyCode === 27) onClose();
// Right
if (e.key === 'Escape') onClose();
Use e.key for readable, modern keyboard handling.
Other useful handlers (quick map)
Definition
React supports the standard DOM event set with camelCase props; choose the handler that matches the user gesture you care about.
In simpler words
You do not need every handler on day one — know the map so you pick the right one later.
Scroll: onScroll (throttle if heavy).
Clipboard: onCopy / onCut / onPaste.
Drag: onDragStart / onDrop (needs preventDefault on dragOver for drop targets).
Media/load: onLoad / onError on images.
Composition (IME): onCompositionStart / onCompositionEnd for CJK input.
Handler cheat sheet
onClick / onDoubleClick → activate
onChange → value changed (inputs)
onFocus / onBlur → entered / left field
onSubmit → form submit (use preventDefault)
onKeyDown / onKeyUp → shortcuts, Escape
onMouseEnter / onMouseLeave→ hover (pair with focus styles)
onPointerDown / onPointerUp→ pointer-accurate interactions
onScroll → scroll position
onPaste → clipboard into a field
Same rule for all of them: pass a function, update state or call Nest from the handler.
Propagation and stopping events
Definition
Events bubble from the target toward ancestors unless stopped; stopPropagation prevents parent handlers from running for that event.
In simpler words
Use stopPropagation sparingly — usually for nested clickable UI (row click vs action button).
preventDefault stops the browser’s default action (link navigate, form submit).
stopPropagation stops React/parent listeners from seeing the event.