Cheat SheetsReactFundamentals

Fundamentals — Cheat Sheet

React · 7 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Fundamentals
React7 topicsQuick revision reference
1

The Mental Model — UI as a Function of State

You describe what the screen should look like for a given state, and React works out what to change. You never write the change itself — that inversion is the whole framework.

  • React is declarative: you describe the UI for a given state and React performs the DOM changes
  • Reaching for the DOM directly is usually a sign that a piece of state is missing
  • Data flows down through props and changes flow up through callbacks — there is no two-way binding
  • Each piece of state has exactly one owner, which is what makes changes traceable
  • React is only the rendering layer; routing, data fetching and the build come from the surrounding stack
Describe the destination, not the journey
// Imperative — you perform each transition yourself
function showResults(results) {
  spinner.style.display = 'none'
  errorBox.style.display = 'none'
  list.innerHTML = ''
  results.forEach(r => list.append(makeRow(r)))
  emptyState.style.display = results.length ? 'none' : 'block'
}
// Every new state means auditing every branch again.

// Declarative — you describe the destination for each state
function Results({ status, results, error }) {
  if (status === 'loading') return <Spinner />
  if (status === 'error')   return <ErrorBox message={error} />
  if (results.length === 0) return <EmptyState />
  return <ul>{results.map(r => <Row key={r.id} {...r} />)}</ul>
}
// Add a state and the compiler and the reader both see the gap.
2

JSX — Markup That Is Really JavaScript

JSX compiles to function calls. Knowing that explains every rule it has, including the ones that look arbitrary.

  • JSX compiles to function calls returning plain objects — every syntax rule follows from that
  • className and htmlFor exist because class and for are reserved JavaScript words
  • Braces accept expressions only; if, for and switch go above the return statement
  • {count && <Badge/>} renders a literal 0 when count is 0 — compare explicitly instead
  • React escapes interpolated values by default; dangerouslySetInnerHTML is the deliberate exception
JSX is function calls
// You write
<button className="primary" onClick={handleRun}>Run</button>

// The compiler produces
jsx('button', { className: 'primary', onClick: handleRun, children: 'Run' })

// which evaluates to a plain object describing what to render:
{ type: 'button', props: { className: 'primary', ... } }

// Consequences that follow directly:
//   - 'class' is a reserved word    -> className
//   - 'for' is a reserved word      -> htmlFor
//   - attributes are JS properties  -> onClick, tabIndex, readOnly
//   - a component returns ONE value -> one root element, or a Fragment

<>                            {/* Fragment — groups without a wrapper div */}
  <Header />
  <Main />
</>

// A capital letter means "component"; lowercase means "DOM element".
<button />   // the HTML button
<Button />   // your component
3

Components and Props

A component is a function taking props and returning UI. Props are read-only inputs, and treating them as read-only is what keeps a React app predictable.

  • A component is a function from props to UI, and must be pure for the same props
  • Props are read-only — copy before sorting or mutating, or you corrupt the parent's data
  • children makes composition the default; prefer it to accumulating boolean flags
  • Split a component when it has its own state, its own reason to change, or a second usage
  • Defining a component inside another component remounts it every render, losing state and focus
Destructure, default, never mutate
function ProblemCard({ problem, locked = false, onOpen }) {
  return (
    <article onClick={() => onOpen(problem.slug)}>
      <h3>{problem.title}</h3>
      <Difficulty level={problem.difficulty} />
      {locked && <ProBadge />}
    </article>
  )
}

<ProblemCard problem={p} onOpen={open} />          // locked defaults to false
<ProblemCard problem={p} locked onOpen={open} />   // shorthand for locked={true}

// Props are read-only. This is a bug, not a shortcut:
function Bad({ items }) {
  items.sort()                     // mutates the parent's array
  return <List items={items} />
}
function Good({ items }) {
  const sorted = [...items].sort() // copy first
  return <List items={sorted} />
}
4

Lists and Keys

A key tells React which item is which between renders. Using the array index is the single most common source of "the wrong row updated" bugs.

  • Keys let React match elements between renders; they must be unique among siblings, not globally
  • An index key attaches state to a position, so deleting or reordering moves state onto the wrong item
  • Index keys are safe only for a static list that never reorders and whose rows hold no state
  • A random key regenerated each render destroys and rebuilds the whole list
  • Changing a component's key deliberately resets all of its internal state
map, with a key from the data
<ul>
  {problems.map(problem => (
    <ProblemRow key={problem.id} problem={problem} />
  ))}
</ul>

// The key goes on the outermost element of the map, not inside it
{rows.map(r => <Fragment key={r.id}><dt>{r.k}</dt><dd>{r.v}</dd></Fragment>)}

// Keys must be unique among SIBLINGS, not globally.
// Two different lists can both use key={1}.

// Filtering and sorting are just JavaScript
{problems
  .filter(p => p.difficulty === level)
  .sort((a, b) => a.order - b.order)
  .map(p => <ProblemRow key={p.id} problem={p} />)}

// Empty state, always
{problems.length === 0
  ? <EmptyState />
  : problems.map(...)}
5

Handling Events

Pass a function, do not call one. Most early event bugs are one pair of parentheses in the wrong place.

  • Event props take a function reference; adding parentheses calls it during render
  • Wrap in an arrow function to pass arguments — inline handlers are rarely a real performance problem
  • React attaches one listener per event type at the root and hands you a normalised SyntheticEvent
  • A document listener you add yourself fires before React handlers — use mousedown for click-outside
  • A clickable div is unreachable by keyboard; use a button, and disable it while a submit is in flight
onClick={fn}, not onClick={fn()}
<button onClick={handleRun}>Run</button>       // correct — a reference
<button onClick={handleRun()}>Run</button>     // WRONG — calls it now

// Passing an argument needs a wrapper
<button onClick={() => onOpen(problem.slug)}>Open</button>

// Inline is fine. "It creates a new function every render" is true
// and almost never the reason a page is slow — measure before
// contorting the code around it.

// The event object
function handleSubmit(e) {
  e.preventDefault()          // stop the browser navigating
  e.stopPropagation()         // stop it reaching parent handlers
  e.currentTarget.value
}

// Common handlers
onChange onInput onSubmit onKeyDown onFocus onBlur
onMouseEnter onPointerDown onScroll
6

Rendering and Reconciliation — What Happens on a State Change

A render is React calling your function, not React touching the DOM. Separating those two ideas explains re-render behaviour, keys, and most performance advice.

  • Rendering calls your function to produce a description; committing applies the minimal DOM changes
  • A re-render is cheap unless it does expensive work or produces actual DOM changes
  • A component re-renders when its own state changes, its parent re-renders, or a consumed context changes
  • Mutating an object does not trigger anything — React compares with Object.is, so pass a new value
  • State belongs to a position and type in the tree, so changing the type or the key resets it
The four steps of an update
// 1. TRIGGER   — initial mount, or a state update in this component
//                 or in one of its ancestors
// 2. RENDER    — React calls your function. It must be PURE: no DOM
//                 writes, no fetches, no mutating outside variables.
//                 The output is a description, not DOM.
// 3. COMMIT    — React diffs against the previous description and
//                 applies only what changed to the real DOM.
// 4. EFFECTS   — after the browser paints, effects run.

// So this component re-renders (function called) but produces the
// same output, and the commit phase does nothing at all:
function Header() { return <h1>AiCanCode</h1> }

// "It re-rendered" is not automatically a problem. The question is
// whether the render did expensive work, or produced DOM changes.
7

Project Structure and Conventions

Group by feature, not by file type. The folder layout that feels tidy at ten components is the one that becomes unnavigable at two hundred.

  • Group by feature rather than by file type, so one change touches one folder
  • Colocate tests, styles and types beside the component they belong to
  • A shared component that imports from a feature is not shared — move it into that feature
  • Hooks must start with "use" for the lint rules to check them; components use PascalCase
  • Path aliases keep imports stable across moves; avoid a global constants file nothing owns
By feature, with shared code pulled out
src/
  features/
    problems/
      ProblemList.jsx
      ProblemCard.jsx
      ProblemCard.test.jsx      // colocated
      useProblems.js            // the feature's own hook
      api.js                    // the feature's own requests
      types.ts
    auth/
      LoginForm.jsx
      useAuth.js
      AuthProvider.jsx
  components/                   // shared, generic, no feature knowledge
    Button.jsx
    Modal.jsx
    Spinner.jsx
  lib/                          // shared non-UI helpers
    apiClient.js
    formatDate.js
  App.jsx

// The test: to delete "problems", delete one folder.
// With type-based folders you would hunt through four.

// A shared component must not import from features/.
// If it needs to, it is not shared — it belongs to that feature.
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/react