Skip to the chapter
←ReactGGroundwork⚛React
React/Beginner/B6
6 / 57 written chapters
/

The cover
  1. B1Setup & JSX
  2. B2Thinking in React
  3. B3Components
  4. B4Props
  5. B5useState
  6. B6Events & conditionals
  7. B7Lists & keys
  8. B8Forms
  9. B9useEffect
  10. B10Lifting state & styling
  11. B11Guided project
  12. ★Cheat page
  1. I1useRef
  2. I2Context
  3. I3useReducer
  4. I4Custom hooks
  5. I5Effects in depth
  6. I6Routing
  7. I7State architecture
  8. I8Memoisation
  9. I9Boundaries & portals
  10. I10Data fetching
  11. I11Forms at scale
  12. I12State libraries
  13. I13Auth
  14. I14TanStack Query
  15. I15Testing
  16. I16TypeScript
  17. I17Animation
  18. I18E2E testing
  19. I19i18n
  20. I20File uploads
  21. I21Real-time UI
  22. I22shadcn/ui
  23. ★Cheat page
  1. A1Fiber
  2. A2Concurrent
  3. A3Suspense
  4. A4Server Components
  5. A5Server Actions
  6. A6use()
  7. A7Rendering strategies
  8. A8Effect timing
  9. A9External stores
  10. A10Refs in depth
  11. A11Component patterns
  12. A12Performance
  13. A13Accessibility
  14. A14Design systems
  15. A15Advanced TypeScript
  16. A16Architecture at scale
  17. A17Custom renderers
  18. A18Migrating & upgrading
  19. A19Production debugging
  20. A20Review & judgement
  21. A21State machines
  22. A22Feature flags
  23. A23Interview bank
  24. ★Cheat page

⚛B6 · Part 1 · Beginner

Events & conditional rendering

Pass the function, do not call it — and know why && puts a zero on your page.

4 min read9 sections6 exercises
6 of 57

In this chapter

  • Handlers are props holding functions
  • The event object
  • Where events actually listen
  • Passing arguments without recreating the world
  • Keyboard, focus and the events worth knowing

and 4 more

about 4 min
09On this page
  1. Handlers are props holding functions
  2. The event object
  3. Where events actually listen
  4. Passing arguments without recreating the world
  5. Keyboard, focus and the events worth knowing
  6. Conditional rendering: four ways
  7. stopPropagation, and when it is the wrong tool
  8. The four states every screen has
  9. Conditional attributes

Handlers are props holding functions

<button onClick={handleClick}>Save</button>      // ✓ passes the function
<button onClick={handleClick()}>Save</button>    // ✗ calls it during render
<button onClick={() => remove(id)}>Delete</button> // ✓ wrap when you need an argument

The second line is the most common beginner bug in React. It runs handleClick while rendering and hands React whatever it returned — usually undefined. If the handler sets state, you get an infinite render loop.

The event object

Handlers receive a SyntheticEvent — React's wrapper with the same API as the native one, normalised across browsers. The real event is at e.nativeEvent if you ever need it.

function handleSubmit(e) {
  e.preventDefault();      // stop the page reloading
  e.stopPropagation();     // stop it bubbling to parents
  console.log(e.target.value, e.currentTarget);
}

e.target is what was actually clicked, which may be a child. e.currentTarget is the element the handler is attached to. Use currentTarget when you want the thing you bound to.

Returning false from a handler does nothing in React — that is a jQuery habit. Call preventDefault().

Where events actually listen

React does not attach a listener to each button. It attaches one listener per event type at the root of your app and works out which component it belongs to when the event bubbles up. That is why handlers are cheap even on a thousand rows, and why a stray stopPropagation on a native listener can stop a React handler firing.

Passing arguments without recreating the world

{rows.map((row) => (
  <Row key={row.id} onSelect={() => select(row.id)} />    // fine
))}

An arrow in JSX creates a new function every render. On a normal list that is irrelevant — making a closure is cheap. It only matters when the child is memoised, because a new function is a new prop and defeats the memo; that is memoisation's problem, not something to pre-optimise here.

The alternative, when you want one stable handler for a whole list:

function handleClick(e) {
  const id = e.currentTarget.dataset.id;   // read it off the DOM
  select(id);
}

<button data-id={row.id} onClick={handleClick}>

Keyboard, focus and the events worth knowing

<input
  onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
  onFocus={...}
  onBlur={...}
/>

<div onClickCapture={...}>      // fires on the way down, before children

e.key is the modern property — "Enter", "Escape", "ArrowDown". keyCode is deprecated. Every React event also has a Capture variant that runs during the capture phase, which is occasionally the only way to intercept something before a child handles it.

Event pooling — where the event object was reused and reading it asynchronously gave you nulls — was removed in React 17. If you find advice about calling e.persist(), it no longer applies.

Conditional rendering: four ways

Ternary, when there are two outcomes

<div>{isLoggedIn ? <Dashboard /> : <Login />}</div>

&&, when there is one

{error && <p className="error">{error}</p>}
The zero trap
{items.length && <List items={items} />}

With an empty list this is 0 && ..., which evaluates to 0 — and React renders 0 as visible text. A stray zero appears on the page and nobody can find where it came from.

{items.length > 0 && <List items={items} />}   // ✓ force a boolean
{items.length ? <List items={items} /> : null}   // ✓ or a ternary

Early return, when the whole component branches

function Profile({ user }) {
  if (!user) return <Spinner />;
  if (user.banned) return <Banned />;

  return <div>{user.name}</div>;   // the happy path, unindented
}

This is usually the most readable option once you have more than two branches, and it keeps the main case at the left margin.

A lookup object, when there are many

const views = {
  loading: <Spinner />,
  error: <ErrorPanel />,
  empty: <Empty />,
  ready: <List items={items} />,
};

return views[status] ?? null;

Cleaner than a chain of ternaries, and it fails loudly if you add a status and forget the view. Note that every branch is constructed here, so keep the elements cheap or use a function per key.

stopPropagation, and when it is the wrong tool

<div onClick={closeMenu}>
  <div className="menu" onClick={(e) => e.stopPropagation()}>   // ✗ fragile
    ...
  </div>
</div>

The click-outside-to-close pattern written with stopPropagation silently breaks anything else listening further up — analytics, a parent's keyboard handling, a library's outside-click detection. Check what was clicked instead:

function handleClick(e) {
  if (menuRef.current?.contains(e.target)) return;   // inside — ignore
  closeMenu();
}

preventDefault and stopPropagation do different things: the first cancels the browser's default action (submitting, navigating, checking a box), the second stops the event travelling. Reaching for the wrong one is a common source of "the form still reloads".

The four states every screen has

Beginner React tends to render only the happy path. A screen that touches data has four, and the last two are the ones users actually hit.

StateWhat the user sees
LoadingA skeleton or spinner — and ideally the shape of what is coming
ErrorWhat went wrong and what to do, not "Something went wrong"
EmptyWhy it is empty and how to fill it, not a blank box
ReadyThe data

Interviewers watching you build a screen are usually counting these. Handling the empty state unprompted is one of the cheapest signals you can give.

Conditional attributes

<button
  className={"btn " + (active ? "is-active" : "")}
  disabled={isSaving}
  aria-pressed={active}
>

disabled={false} removes the attribute entirely — React understands boolean DOM attributes, so you never need {isSaving ? "disabled" : ""}.

Interview answer, one sentence

"Handlers are props holding function references, so you pass the function rather than calling it, and React delegates the real listeners to the root. For conditionals, && is fine as long as the left side is a boolean — with a number, zero renders."

06Practice this chapter

Opens in the editor: write it, run it, and check it against real tests.

  • 10 testsBuild a className conditionallybeginner
  • 10 testsOne name for the state you're inbeginner
  • 8 testsFix the stray zerobeginner
  • 8 testsAn accessible show/hide sectionbeginner
  • 9 testsA star rating with hover previewbeginner
  • 5 testsA document-level shortcut, cleaned up properlybeginner
All 46 for this level →
B6

Finished reading?

0 of 57 chapters read in this series

→Next stop

  1. B5Previous · B5State with useState
  2. B6you are here
  3. Next · B7Lists with .map() and keysA key is not for you and it is not for performance — it tells React which item is which.4 min read

01On this page

9 sections · 0%

  1. 01Handlers are props holding functions
  2. 02The event object
  3. 03Where events actually listen
  4. 04Passing arguments without recreating the world
  5. 05Keyboard, focus and the events worth knowing
  6. 06Conditional rendering: four ways
  7. 07stopPropagation, and when it is the wrong tool
  8. 08The four states every screen has
  9. 09Conditional attributes

[] chapters · t top