Fullstack CourseLearn by building
Back to week 1

Topic

Responding to events

Definition

Event handlers are callback functions React invokes in response to browser interactions such as clicks, typing, focus changes, and form submission.

In simpler words

You pass a function (onClick, onChange, onBlur, …). React calls it later when the user acts — not while rendering.

Learn the common React DOM handlers with examples, then the mistakes that cause the most bugs.

After this you can

  • Wire onClick, onChange, onBlur, onFocus, onSubmit, and keyboard handlers correctly
  • Pass arguments without calling the handler during render
  • Stop default form navigation and reason about bubbling
  • Spot high-bug patterns (calling during render, missing preventDefault, blur vs change)

What an event handler is

Definition

An event handler is a function React stores on a prop (for example onClick) and invokes when the matching browser event occurs.

In simpler words

Pass the function. Do not call it while building JSX unless you intentionally want it to run during render.

React uses camelCase names (onClick, not onclick) and receives a SyntheticEvent wrapper around the native event.

Handlers are the right place to update state, call props callbacks, or start a Nest mutation.

Pass vs call

// Correct — React calls later
<button onClick={handleSave}>Save</button>
<button onClick={() => deleteTicket(id)}>Delete</button>

// Wrong — runs during render
<button onClick={deleteTicket(id)}>Delete</button>

onClick={fn()} evaluates immediately. Wrap with () => … when you need arguments.

Mistake: calling the handler while rendering

// Wrong — runs during render
<button onClick={deleteTicket(id)}>Delete</button>

// Right — runs on click
<button onClick={() => deleteTicket(id)}>Delete</button>

Parentheses call the function immediately. Pass a function reference or wrap it in an arrow.

Pointer and click handlers

Definition

Pointer handlers respond to mouse or pointer activation on an element (onClick, onDoubleClick, onMouseEnter/Leave, onPointerDown/Up).

In simpler words

Use onClick for primary actions. Prefer type="button" inside forms so a click does not submit by accident.

onClick is the everyday action handler for buttons and interactive rows.

onMouseEnter / onMouseLeave are useful for hover UI, but keyboard users need an equivalent (focus styles or onFocus).

onClick + type="button"

function RowActions({ onDelete }) {
  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Save</button>
      <button type="button" onClick={onDelete}>
        Delete
      </button>
    </form>
  );
}

Without type="button", a button inside a form defaults to submit and can POST/reload.

Mistake: button defaults to submit

// Wrong — deletes AND submits the form
<form onSubmit={save}>
  <button onClick={onDelete}>Delete</button>
</form>

// Right
<form onSubmit={save}>
  <button type="button" onClick={onDelete}>Delete</button>
  <button type="submit">Save</button>
</form>

Always set type explicitly inside forms.

Typing: onChange and controlled inputs

Definition

onChange fires when the control’s value changes; for controlled inputs it updates React state that drives value={…}.

In simpler words

Every keystroke (or select change) should call a setter so the displayed value and state stay the same.

Read text with e.target.value (or e.currentTarget.value).

Checkboxes usually use e.target.checked.

Controlled text + checkbox

const [title, setTitle] = useState('');
const [urgent, setUrgent] = useState(false);

<input
  value={title}
  onChange={(e) => setTitle(e.target.value)}
/>
<input
  type="checkbox"
  checked={urgent}
  onChange={(e) => setUrgent(e.target.checked)}
/>

value/checked come from state; onChange writes state. That is a controlled input.

Mistake: value without onChange

// Wrong — React warns; typing feels stuck
<input value={title} />

// Right
<input value={title} onChange={(e) => setTitle(e.target.value)} />

Controlled inputs need both sides of the binding.

Focus: onFocus and onBlur

Definition

onFocus runs when an element receives focus; onBlur runs when it loses focus (tab away, click elsewhere, or programmatically blur).

In simpler words

Use blur for “validate when the user leaves the field”; use focus for hints or clearing errors when they return.

Blur validation is friendlier than validating on every keystroke for long forms.

RelatedTarget on focus/blur events can tell you where focus is going (useful for dropdowns).

Validate on blur

const [email, setEmail] = useState('');
const [error, setError] = useState<string | null>(null);

function handleBlur() {
  if (!email.includes('@')) {
    setError('Enter a valid email');
  } else {
    setError(null);
  }
}

<label>
  Email
  <input
    value={email}
    onChange={(e) => setEmail(e.target.value)}
    onBlur={handleBlur}
    onFocus={() => setError(null)}
  />
</label>
{error && <p role="alert">{error}</p>}

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.

Row click vs delete button

<li onClick={() => openTicket(id)}>
  {title}
  <button
    type="button"
    onClick={(e) => {
      e.stopPropagation();
      deleteTicket(id);
    }}
  >
    Delete
  </button>
</li>

Without stopPropagation, delete also opens the ticket.

Mistake: missing stopPropagation

// Wrong — delete also triggers row open
<li onClick={() => open(id)}>
  <button type="button" onClick={() => remove(id)}>X</button>
</li>

// Right
<button
  type="button"
  onClick={(e) => {
    e.stopPropagation();
    remove(id);
  }}
>
  X
</button>

Stop bubbling only at the nested control that should not trigger the parent.

Live playground

Events sandbox

Try click, change, blur, and submit — watch the event log.

    Try tab away (blur), type, Escape, submit, and click.

    Keep in mind

    • Pass functions; do not call them while rendering.
    • Forms: onSubmit + preventDefault; buttons that should not submit: type="button".
    • Blur = leave-field validation; Change = live value; Focus = enter-field UX.
    • When Nest is involved, the handler starts the request — rendering never should.

    Test

    Check your understanding

    At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥ 80% (enforced by the API) to save progress.

    Checking your session…

    10 questions · concept 2 · syntax 3 · practical 3 · logic 2

    Concept1. What is an event handler in React?
    Syntax2. Which wires a click correctly when you need an id argument?
    Practical3. Best place to validate an email when the user leaves the field?
    Practical4. Clearing a field error when the user returns to the input fits…
    Syntax5. Controlled text input pairing?
    Logic6. Why call e.preventDefault() in onSubmit for an SPA form?
    Practical7. Button inside a form that must NOT submit should use…
    Concept8. onChange vs onBlur for search-as-you-type?
    Logic9. Delete button inside a clickable row opens the row too. Likely fix?
    Syntax10. Keyboard Escape to close a dialog typically uses…