← React for Beginners

Why React exists

Lesson 01 of 12 · 10 min

Most React tutorials open by telling you what React is. That is the wrong end to start from, because “a library for building user interfaces” describes jQuery, Angular, Vue, Svelte and a for loop that writes HTML strings. It does not tell you why anyone bothered to build another one.

So, the problem. It is small, boring, extremely common, and once you have felt it, everything React does afterwards is obvious.

The problem: a screen and some data that must agree

Here is a shopping list. There is an array of items, a list on the page, a counter that says how many there are, and a message that appears when the list is empty.

Written by hand, with no library at all:

<ul id="list"></ul>
<p id="count"></p>
<p id="empty">Nothing here yet.</p>
<button id="add">Add milk</button>
let items = [];
let nextId = 1;

document.querySelector("#add").addEventListener("click", () => {
  const item = { id: nextId++, name: "Milk" };
  items.push(item);

  const li = document.createElement("li");
  li.textContent = item.name;
  document.querySelector("#list").append(li);

  document.querySelector("#count").textContent = `${items.length} items`;
  document.querySelector("#empty").hidden = items.length > 0;
});

That works - mostly. Load the page and the counter is blank, because the only code that writes it lives inside the click handler and nobody has clicked yet. That is the first hint of what is wrong here, and we have not even added a second feature.

Now add a “remove” button.

You have to write a second block of code that does the reverse of the first: splice the array, find and remove the right <li>, update the counter, and unhide the empty message. Then add a “clear all” button and write a third. Then somebody asks for a filter, and each of those three blocks has to learn about it.

The truth about the list exists in two places at once - the items array, and the DOM, which is the live tree of elements the browser is actually showing - and every single thing that can change it has to update both, by hand, in the right order, forever. There is no code anywhere in that file that says “the counter shows the number of items”. That fact lives, unwritten, spread across every handler that remembers to maintain it, plus the page-load path that forgot to.

Which means the interesting failure is not a crash. You add the filter, you update two of the three handlers, and now the page says 2 items while three of them are visible. Nothing threw, nothing logged. The screen and the data have quietly disagreed, and the only way to find out is to look.

The same fact stored twice: an items array and the page. Every handler must update both by hand, and the one that forgets leaves the counter disagreeing with the list. Your data The page items = [ Milk, Bread, Eggs ] addNote removeNote filterNotes forgot the counter • Milk • Bread • Eggs 2 items ← wrong Three rows on screen. A counter that says two. Nothing threw. No code anywhere says "the counter shows the number of items" - that fact only exists in whichever handlers remembered it.
The bug is not the crash you can find. It is the quiet disagreement between two copies of one fact.

That is the problem. Not “the DOM API is verbose” - it is a bit verbose, but that is a typing problem and typing problems are cheap. The expensive problem is that keeping two representations of the same fact in sync is work that grows with every feature you add, and it is work a computer should be doing.

Two ways to describe a screen

What we wrote above is imperative: a list of instructions for changing the page. Create this element. Set that text. Hide this paragraph. Each instruction assumes it knows what the page looked like a moment ago, which is why adding a new way to change the data means revisiting all the old instructions.

The alternative is declarative: instead of instructions for changing the page, you write one description of what the page should look like for a given set of data. Not “append an li” - rather “for this array, the page looks like this”.

function ShoppingList({ items }) {
  return (
    <div>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
      <p>{items.length} items</p>
      {items.length === 0 && <p>Nothing here yet.</p>}
    </div>
  );
}

You will not understand every character of that yet, and you are not meant to. The HTML-looking syntax inside JavaScript is called JSX (lesson 3). The braces around items are unpacking it out of the single object React hands to every component, which is called props (lesson 4). And key is how React tells list items apart between updates (lesson 5).

What matters right now is the shape. There is exactly one place that says the counter shows items.length. There is exactly one place that says the empty message appears when the list is empty. Adding a remove button does not touch this function at all, because this function does not care how the array changed - only what is in it now.

That is the whole trade. You stop writing the steps and start writing the result, and something else works out the steps.

So who works out the steps?

React does, and the mechanism is less clever than people expect.

When the data changes, React calls your function again. It gets back a fresh description of what the page should be, compares it against the description from last time, and changes only the parts of the real DOM that differ. Running your function and producing that description is called a render. It does not mean anything appeared on screen - it means React asked your code what the screen should look like.

The folklore overstates that comparison. React is not computing the theoretically smallest set of edits; it walks the two descriptions in step and uses some cheap rules to decide what matches what. That is why key exists at all - for a list, the rules need a hint from you. A genuinely minimal diff would need no such hint, and would cost far more to compute than the DOM work it saved.

What that buys you is that the counter and the list can no longer disagree, because they are computed from the same array in the same pass. The bug from the first half of this lesson is not a bug you fix in React. It is a bug you cannot write.

The cost is that React re-runs your function more often than you would have re-run your own hand-written update, and does a comparison you would not have done. For virtually every interface a person will use, that is an excellent trade - the comparison is fast and your time is not. It is worth knowing the trade exists, though, because it is the reason the answer to “should I use React for this?” is not always yes.

Components

ShoppingList above is a function that takes data and returns a description of some UI. That is a component, and it is the only unit of structure React has. There is no separate concept for a page, a widget, a template or a partial. A button is a component. A page is a component. A page is built by putting smaller components inside it, exactly the way you build any program out of smaller functions.

Because a component is just a function, everything you already know about functions carries over: you can name them, move them into their own files, reuse them, pass different data to get different results, and test them by calling them. There is no new organising system to learn. The one you already know, functions, is the one React uses.

When not to use React

If a page is mostly text and has two interactive things on it, React is the wrong tool. Ten lines of addEventListener beats a build step, a dependency tree and a runtime for that. The site you are reading this on ships almost no JavaScript at all, on purpose.

React earns its cost when several parts of the screen depend on the same changing data - what React calls state, and what lesson 7 is entirely about - and the thing you are building has enough moving pieces that keeping them in agreement by hand has become the job. A dashboard, an editor, anything with a form worth the name, anything with a list you can filter and sort and add to. The moment you catch yourself writing the third handler that has to remember to update the counter, you have arrived.

Recap

The problem React solves is not verbosity, it is synchronisation: when the same fact lives in your data and in the DOM, every feature you add is another chance for the two to drift apart, silently. React’s answer is to let you describe what the screen should be for a given set of data, and to work out the DOM changes itself - so the two cannot drift, because there is only one of them. Components are how that description gets broken into pieces, and a component is just a function.

Next: getting a real React project running on your machine, and what every file in it is for.