Why React exists

Understand the one idea React is built on, and when it is worth the cost.

01of 20
≈ 9Min read
7Parts

The problem

Writing a page by hand means writing every transition: when the count changes, find the element, update its text, toggle a class, maybe remove a row. Each new piece of state multiplies the number of transitions you have to get right.

the same feature, by hand
let items = [];
let filter = "";

function addItem(text) {
  items.push({ id: nextId++, text, done: false });
  const li = document.createElement("li");
  li.textContent = text;
  list.append(li);
  count.textContent = items.length;
  empty.hidden = items.length > 0;
  if (filter && !text.includes(filter)) li.hidden = true;
}

function toggle(id) {
  const item = items.find((i) => i.id === id);
  item.done = !item.done;
  document.querySelector(`[data-id="${id}"]`).classList.toggle("done", item.done);
  count.textContent = items.filter((i) => !i.done).length;   // …and so on
}

Every function has to remember every consequence. Miss one and the screen disagrees with the data — the bug that is hardest to find, because nothing is broken except what you can see.

The idea

React turns it around. You write one function from state to markup, and when the state changes React calls it again and updates whatever actually differs.

the same feature, described
function List({ items, filter }) {
  const shown = items.filter((i) => i.text.includes(filter));

  return (
    <>
      <p>{shown.filter((i) => !i.done).length} left</p>
      {shown.length === 0 && <p>Nothing here yet.</p>}
      <ul>
        {shown.map((item) => (
          <li key={item.id} className={item.done ? "done" : ""}>{item.text}</li>
        ))}
      </ul>
    </>
  );
}

There is no addItem that has to remember the counter, the empty state and the filter. There is one description of what the screen looks like for any state, and React works out the difference.

What that buys you

  • The screen cannot disagree with the data. It is computed from it.
  • A new feature is a new expression, not a new set of transitions in five functions.
  • Components compose. A list of cards is a Card used many times, with its own state.
  • You stop querying the DOM. Almost no querySelector in a React codebase, and no ids to keep in step.

What it costs

  • A build step and a dependency, where a plain page needs neither.
  • About 45 KB of JavaScript before your own code.
  • A new set of rules to learn — hooks, keys, dependencies, re-renders — and new ways to be wrong.
  • Everything happens after JavaScript loads, unless you add a framework that renders on the server.

When it is not worth it

A marketing page, a blog, a form, a documentation site: these have little state and a lot of content. Plain HTML with a few hundred lines of JavaScript is faster to build, faster to load and easier to keep working.

React earns its cost when the same data appears in several places at once, when state changes often, and when the interface is assembled from pieces that repeat — a dashboard, an editor, an application.

The honest test

Count the pieces of state and the places each one appears. One or two, in one place each: write it by hand. A dozen, appearing in several places: that is what React is for.

What React is not

  • Not a framework. It renders components. Routing, data fetching, forms and building are separate choices — which is why Next.js exists.
  • Not fast by default. It is fast enough, and it makes the slow thing (manual DOM work) unnecessary. A badly written React app is slower than a well-written plain one.
  • Not a way to avoid learning the DOM. Everything in the HTML, CSS and JavaScript courses still applies — React sits on top of it, and when something is wrong you debug the DOM.

The mental model

Everything else in this course is a detail of those three sentences.

three sentences
// 1. A component is a function that returns what to show.
function Badge({ count }) {
  return <span className="badge">{count}</span>;
}

// 2. Props come in from the parent. They are read-only.
<Badge count={3} />

// 3. State belongs to a component. Changing it re-runs the function.
const [count, setCount] = useState(0);
setCount(count + 1);    // React calls the component again

Practice