Render and commit
Follow one click through React: what triggers a render, which components React calls and in what order, what the commit changes, and when the browser paints.
- 01Name what triggers a renderTell the first render, started by
root.render, from re-renders that set functions ask for, and explain why several updates in one event make one render. - 02Follow a renderPredict which components React calls and in what order, and show that rendering never changes the page by itself, StrictMode's double call included.
- 03Explain the commit and the paintSay what the commit changes on a first render and on a re-render, why the page never shows half an update, and when the browser paints.
One click, three steps, in the console
How React works: a first look walked once along the path from a click to the screen. This lesson stops at each step and looks closely, with code that prints what React is doing as it does it. Every screen update in React has three steps:
- Trigger. Something asks React for a render: the app starting, or a set function from state.
- Render. React calls your components to find out what the screen should show.
- Commit. React changes the page, the DOM, where the new result differs from the last one.
After that, the browser paints: it draws the changed page on the screen. Here is a small tea order. Every component prints a line when React calls it, and the second tab, watchDom.js, prints a DOM: line for every change to the page and flashes the part that changed. Look at the console under the result, then click Add one.
import { useState } from "react";import "./watchDom.js"; function Header() { console.log("Render Header"); return <h3>Tea Corner</h3>;} function QuantityPicker({ quantity, onAdd }) { console.log("Render QuantityPicker"); return ( <p> Green tea: <strong>{quantity}</strong>{" "} <button onClick={onAdd}>Add one</button> </p> );} function PriceTag({ total }) { console.log("Render PriceTag"); return <p>Total: ${total}</p>;} function OrderPanel() { const [quantity, setQuantity] = useState(1); console.log("Render OrderPanel"); return ( <div> <QuantityPicker quantity={quantity} onAdd={() => setQuantity(quantity + 1)} /> <PriceTag total={quantity * 4} /> </div> );} export default function App() { console.log("Render App"); return ( <section> <Header /> <OrderPanel /> </section> );}OrderPanel keeps the quantity on line 25. Its children show it: QuantityPicker on lines 9 to 17 with the button, and PriceTag on lines 19 to 22 with the total. Each component's first line logs its name. Line 2 imports watchDom.js, which uses a MutationObserver, the browser's tool for watching part of a page, to print each change React makes.
When the result first appears, the console lists all five components, from Render App to Render PriceTag, each with a small 2 beside it (StrictMode calls each component twice in development; the Keeping components pure lesson explains why, and this page shows a repeated line once, with the count). Then one line: DOM: added <section>.
Now the click. The console adds Render OrderPanel, Render QuantityPicker, and Render PriceTag, and only after all three, DOM: text "1" → "2" and DOM: text "4" → "8". The number and the total flash. That's the three steps in order: the click triggered a render, React called three components, and then it committed two changes.
App and Header don't render.A tea shop changes its window display. The designer redraws the whole window on paper, while the real window stays as it is. Then the decorator compares the new sketch with the last one and moves only the things that changed. Only when the work is done does the blind go up.
- In real life: The manager asks for one more teapot in the window
- In React: A click calls
setQuantity(2): a trigger - In real life: The designer sketches the whole window again, on paper
- In React: React calls your components, which describe the whole result: the render
- In real life: Nothing in the window moves while they sketch
- In React: The page doesn't change during the render
- In real life: The decorator moves only what differs from the last sketch
- In React: React changes only the DOM nodes that differ: the commit
- In real life: The blind goes up, and people outside see the new window
- In React: The browser paints the new frame
Every update is trigger, render, commit. A trigger asks for a render. In the render, React calls your components, and the page doesn't change. In the commit, React changes only the DOM nodes that differ from the last render. Then the browser paints.
Step 1: something asks for a render
There are two triggers. The first is the app starting. Every React app has an entry file that hands React an element on the page and asks for the first render. Every live example in this course has one; this one shows it, with the component in the App.jsx tab.
import { StrictMode } from "react";import { createRoot } from "react-dom/client";import App from "./App.jsx";import "./watchDom.js"; const root = createRoot(document.getElementById("root"));root.render( <StrictMode> <App /> </StrictMode>,);Line 6 hands React the page's <div id="root">, and line 7's root.render asks for the first render of App, inside StrictMode (lines 8 to 10). Line 4 starts watching the page before that, so the console shows Render App with quantity 1, then DOM: added <p>. Press Edit & run and put // in front of lines 7 to 11: the result goes blank and the console stays empty. Nothing asked for a render.
The second trigger is a state update: calling a set function, like setQuantity. React then renders the component that owns that state. Nothing else triggers a render. Changing a variable, changing an array in place, or changing the page yourself doesn't tell React anything.
A set function doesn't render right away. It puts the new value in a queue and asks for a render, and React waits until the code that's running finishes. So several set calls make one render. This is called batching. Click Make it a gift, then Change it in a second and wait.
import { useState } from "react"; export default function GiftOptions() { const [quantity, setQuantity] = useState(1); const [giftWrap, setGiftWrap] = useState(false); const [note, setNote] = useState("none"); console.log(`Render: ${quantity} teas, gift wrap ${giftWrap}, note ${note}`); function makeItAGift() { setQuantity(2); setGiftWrap(true); setNote("Happy birthday!"); } function changeItLater() { setTimeout(() => { setQuantity(3); setGiftWrap(false); setNote("See you soon!"); }, 1000); } return ( <div> <p>{quantity} teas, gift wrap: {giftWrap ? "yes" : "no"}</p> <p>Note: {note}</p> <button onClick={makeItAGift}>Make it a gift</button> <button onClick={changeItLater}>Change it in a second</button> </div> );}makeItAGift on lines 9 to 13 calls three set functions. The console adds one line, Render: 2 teas, gift wrap true, note Happy birthday!, not three: React rendered once, with all three new values. changeItLater on lines 15 to 21 does the same inside a timer, and a second later you get one line again: Render: 3 teas, gift wrap false, note See you soon!.
Batching in timers and promises is called automatic batching, and it arrived in React 18. Older React batched only inside its own event handlers, and rendered once for each set call anywhere else; you may still read about that. In React 19, updates are batched by default, wherever they come from. Batching also means the page never shows a mix, like the new quantity with the old note. The Queueing a series of state updates lesson goes further, with several updates to the same value.
Step 2: React calls your components
Rendering means React calling your component functions. On the first render it starts at the root. On a re-render it starts at the component whose state changed. Either way it goes down: when a component returns other components, React calls those, then the ones they return, until it reaches plain HTML tags. In the tea order, the click changed OrderPanel's state, so React called OrderPanel, then QuantityPicker, then PriceTag, in the order they appear in the JSX.
OrderPanel, whose state changed, then calls its children in order: QuantityPicker, then PriceTag. App and Header are above and beside it, so this render leaves them out.Two things follow. Children render with their parent, even when their props are the same as before. And components above or beside the one that changed don't render: App and Header printed nothing on the click. The Your UI as a tree lesson has a playground for this, and the memo, useMemo and useCallback lesson shows how to skip renders you don't need.
While React renders, the page doesn't change. This example reads the page from inside the component, which you should never do in real code, just to see what's on the page during the render. Click Add one twice.
import { useState } from "react"; export default function App() { const [quantity, setQuantity] = useState(1); // Only for this experiment: read the page while React renders. const onPage = document.querySelector("strong")?.textContent ?? "nothing yet"; console.log(`Rendering ${quantity}. The page shows: ${onPage}`); return ( <p> Green tea: <strong>{quantity}</strong>{" "} <button onClick={() => setQuantity(quantity + 1)}>Add one</button> </p> );}Line 6 finds the <strong> on the page and reads its text, or nothing yet if it isn't there. Line 7 prints it next to the quantity this render works with. The first render prints Rendering 1. The page shows: nothing yet: the paragraph isn't on the page until the first commit. The first click prints Rendering 2. The page shows: 1. React is working out the screen for 2, and the page still shows 1; the commit comes after.
That's also why React can call a component more than once without harm, as long as rendering only works things out. StrictMode does exactly that in development: it calls each component twice and keeps the second result, but commits once. Look at the overview's console again: every Render line has the 2, and every DOM: line appears once. In the main.jsx example above, press Edit & run and replace lines 7 to 11 with root.render(<App />);: the render lines lose their 2. A production build calls each component once.
The playground runs a similar order, and pauses one click at every step. Each row shows the page's paragraphs at that moment. Press Add one, then Add one as a gift.
function QuantityPicker({ quantity }) { return <p>Green tea: <strong>{quantity}</strong></p>;} function PriceTag({ total }) { return <p>Total: ${total}</p>;} function GiftNote({ giftWrap }) { return <p>Gift wrap: {giftWrap ? "yes" : "no"}</p>;} function OrderPanel() { const [quantity, setQuantity] = useState(1); const [giftWrap, setGiftWrap] = useState(false); function addGift() { setQuantity(quantity + 1); setGiftWrap(true); } return ( <div> <QuantityPicker quantity={quantity} /> <PriceTag total={quantity * 4 + (giftWrap ? 2 : 0)} /> <GiftNote giftWrap={giftWrap} /> <button onClick={() => setQuantity(quantity + 1)}>Add one</button> <button onClick={addGift}>Add one as a gift</button> </div> );} function App() { return ( <section> <h3>Tea Corner</h3> <OrderPanel /> </section> );}click a buttonTea Corner
Green tea: 1
Total: $4
Gift wrap: no
Use the two buttons in the order.
Press Add one in the order, then Add one as a gift. Each row is one step of your click, with what the page showed at that moment.
For Add one as a gift, the handler on lines 17 to 20 calls two set functions, and there is still one render pass: OrderPanel, QuantityPicker, PriceTag, GiftNote, in that order. In every render row, the page still shows the old values. The commit row is the first where the page changes, and it makes all three changes together: the quantity, the total, and the gift note. App never appears after the first render: its state didn't change.
Step 3: React changes only what differs
After the render, React commits: it applies the result to the DOM. What it does depends on the render.
- The first render. There is nothing on the page yet. React makes every DOM element the components described, puts them together off the page, and then adds the whole tree in one go. That's why the overview printed a single line,
DOM: added <section>, though the section holds a heading, two paragraphs, and a button. - A re-render. While rendering, React compared each new element with the one in the same place last time, and marked the ones that may have changed. The commit visits only those, compares their old and new props, and changes only what differs: in the overview, two text nodes. The heading, the button, and the rest of each paragraph stay the same DOM nodes. If nothing differs, the commit changes nothing.
<section> to the page in one change. On a re-render, it changes only the two text nodes that differ; the heading and the button stay as they are.How to read the table: each row asks one question of the first render and of a re-render.
| Question | First render | Re-render |
|---|---|---|
| What triggers it? | root.render(<App />), once, when the app starts. | A set function, like setQuantity(2), from a click, a timer, or an answer from a server. |
| Which components does React call? | Every component, starting at the root. | The one whose state changed, and every component inside it. |
| What does React compare the result with? | Nothing. There is no earlier description yet. | The description from the last render. |
| What does the commit do? | It makes every element, then adds the whole tree to the page in one change. | It changes only the nodes that differ, and nothing at all when nothing differs. |
React works out the changes by comparing its new description with its last description, not with the page. It doesn't read the DOM to decide. This experiment changes the page behind React's back, which you should never do in real code. Click Change the page by hand, then Add one.
import { useState } from "react";import "./watchDom.js"; export default function App() { const [quantity, setQuantity] = useState(1); function changeByHand() { // Only for this experiment: change React's elements yourself. document.querySelector("h3").style.color = "tomato"; document.querySelector("strong").textContent = "99"; } return ( <div> <h3>Green tea</h3> <p>Quantity: <strong>{quantity}</strong></p> <button onClick={changeByHand}>Change the page by hand</button> <button onClick={() => setQuantity(quantity + 1)}>Add one</button> </div> );}Lines 9 and 10 turn the heading tomato red and put 99 in the <strong>. The console shows those changes too: watchDom.js sees every change, React's or not. Then Add one renders App with quantity 2. Compared with the last render, only the number differs, so the commit prints DOM: text "99" → "2": React overwrote your 99 because it changes that text. The heading stays red: its description didn't change, so React never touched it. When you need the real DOM element, for focus or measuring, React gives you refs; Manipulating the DOM with refs covers them.
After the commit: the browser paints
React's work ends with the commit. Drawing the pixels is the browser's job, and React's docs call it painting, to keep it apart from React's render. The browser paints between pieces of work: while your JavaScript runs, from the click handler through the render and the commit, it can't draw. So the page never shows half an update. This price tag takes 300 milliseconds to render, on purpose. Click Add one.
import { useState } from "react";import "./watchDom.js"; function SlowPriceTag({ total }) { const start = performance.now(); while (performance.now() - start < 300) { // Busy on purpose: this component takes 300 milliseconds to render. } return <p>Total: ${total}</p>;} export default function App() { const [quantity, setQuantity] = useState(1); return ( <div> <p>Green tea: <strong>{quantity}</strong></p> <SlowPriceTag total={quantity * 4} /> <button onClick={() => setQuantity(quantity + 1)}>Add one</button> </div> );}Lines 5 to 8 keep the computer busy for 300 milliseconds each time SlowPriceTag renders, twice with StrictMode. The quantity on line 17 has nothing slow about it, yet it doesn't change first: for more than half a second nothing moves, and then the quantity and the total change in the same frame, both flashing. The commit made both changes, and the paint showed them together.
Some code needs to run after the commit, like a timer to start or a size to measure. That's what Effects are for; a layout Effect even runs right after React changes the page, before the paint. Synchronizing with effects and Layout effects and timing cover both, and the Rendering and the event loop lesson shows when browsers paint.
A coupon that never shows half an update
Checkout pages often wait for a server. Here, applying a coupon shows a message while it checks, then updates the total and the message together. Click Apply TEA10 and watch the console.
import { useState } from "react";import "./watchDom.js"; function checkCoupon(code) { // Stands in for a request to a server: it answers after half a second. return new Promise((resolve) => { setTimeout(() => resolve(code === "TEA10" ? 10 : 0), 500); });} export default function Checkout() { const [discount, setDiscount] = useState(0); const [message, setMessage] = useState("No coupon yet"); const [checking, setChecking] = useState(false); console.log(`Render: ${discount}% off, checking ${checking}`); async function applyCoupon() { setChecking(true); const percent = await checkCoupon("TEA10"); setDiscount(percent); setMessage(`Coupon applied: ${percent}% off`); setChecking(false); } return ( <div> <p>Total: ${20 - (20 * discount) / 100}</p> <p>{checking ? "Checking the coupon" : message}</p> <button onClick={applyCoupon} disabled={checking}>Apply TEA10</button> </div> );}checkCoupon on lines 4 to 9 stands in for a server: a promise that answers 10 after half a second. applyCoupon on lines 17 to 23 is an async function. Line 18 sets checking, and the handler pauses at the await on line 19, so React renders right away: Render: 0% off, checking true, then the message changes to Checking the coupon and the button turns off (DOM: set disabled on <button>).
Half a second later, lines 20 to 22 call three set functions. Automatic batching makes them one render, Render: 10% off, checking false, and one commit with three changes: the total goes from 20 to 18, the message becomes Coupon applied: 10% off, and the button turns back on. The shopper never sees the new total next to Checking the coupon, or the old total next to the applied coupon.
Sort what asks React for a render, and what doesn't:
setQuantity(quantity + 1)root.render(<App />)setTimeout(() => setNote("Thanks!"), 1000)- A parent's set function, for its child
quantity = quantity + 1items.push("Tea")document.title = "Cart (2)"props.price = 5
Sort each line of code or event: does it ask React to render?
Common mix-ups
- "Rendering means changing the page." No. Rendering is React calling your components. The page changes in the commit, after the render, and only where something differs.
- "Each set call renders right away." No. A set call asks for a render. React batches the calls and renders once, after the code that's running finishes, in timers and promises too.
- "React renders the whole app on every update." No. It starts at the component whose state changed, and renders that component and everything inside it. Its parents and siblings don't render.
- "StrictMode makes React update the page twice." No. In development, it calls your components twice; the commit still happens once, and a production build calls them once.
- "React reads the page to find what changed." No. It compares the new render with the last render. That's why a change you make by hand can stay, or be overwritten, depending on what React changes next.
- "The screen shows each change as React makes it." No. The browser paints after your JavaScript finishes, so all of a commit's changes appear in the same frame.
Practice exercises
Predict first, then press Edit & run and read the console under the result. Remember StrictMode's 2: a render line with a 2 beside it is one render.
You click Save once. How many times does React render Profile?
import { useState } from "react";
export default function Profile() {
const [name, setName] = useState("Asha");
const [city, setCity] = useState("Pune");
const [saved, setSaved] = useState(false);
console.log("Render Profile");
function save() {
setName("Asha Rao");
setCity("Mumbai");
setSaved(true);
}
return (
<div>
<p>{name} from {city}</p>
<button onClick={save}>{saved ? "Saved" : "Save"}</button>
</div>
);
}import { useState } from "react"; export default function Profile() { const [name, setName] = useState("Asha"); const [city, setCity] = useState("Pune"); const [saved, setSaved] = useState(false); console.log("Render Profile"); function save() { setName("Asha Rao"); setCity("Mumbai"); setSaved(true); } return ( <div> <p>{name} from {city}</p> <button onClick={save}>{saved ? "Saved" : "Save"}</button> </div> );}The three set calls in save are batched: React renders Profile once, after the handler, with all three new values. The console adds one Render Profile line, with StrictMode's 2 beside it.
When this page first loads, how many DOM lines does the console print?
import "./watchDom.js";
export default function Menu() {
return (
<main>
<h3>Today's teas</h3>
<ul>
<li>Green tea</li>
<li>Mint tea</li>
</ul>
</main>
);
}import "./watchDom.js"; export default function Menu() { return ( <main> <h3>Today's teas</h3> <ul> <li>Green tea</li> <li>Mint tea</li> </ul> </main> );}React makes the <main>, the heading, the list, and both items off the page, then adds <main> to the root in one change. The console prints one line: DOM: added <main>.
Clear the console, click Add tea, and read the new render lines. Which components does React call? Answer first, then check.
import { useState } from "react"; function Header() { console.log("Render Header"); return <h3>Tea Corner</h3>;} function BasketCount({ count }) { console.log("Render BasketCount"); return <p>In your basket: {count}</p>;} function Basket() { const [count, setCount] = useState(0); console.log("Render Basket"); return ( <div> <BasketCount count={count} /> <button onClick={() => setCount(count + 1)}>Add tea</button> </div> );} function Footer() { console.log("Render Footer"); return <p>Open every day</p>;} export default function App() { console.log("Render App"); return ( <> <Header /> <Basket /> <Footer /> </> );}import { useState } from "react"; function Header() { console.log("Render Header"); return <h3>Tea Corner</h3>;} function BasketCount({ count }) { console.log("Render BasketCount"); return <p>In your basket: {count}</p>;} function Basket() { const [count, setCount] = useState(0); console.log("Render Basket"); return ( <div> <BasketCount count={count} /> <button onClick={() => setCount(count + 1)}>Add tea</button> </div> );} function Footer() { console.log("Render Footer"); return <p>Open every day</p>;} export default function App() { console.log("Render App"); return ( <> <Header /> <Basket /> <Footer /> </> );}The click changes Basket's state, so React calls Basket, then BasketCount inside it. App, Header, and Footer print nothing: they're above and beside Basket.
Change main.jsx so React renders App without StrictMode. After your change, how many render lines does one click print?
import { StrictMode } from "react";import { createRoot } from "react-dom/client";import App from "./App.jsx";import "./watchDom.js"; const root = createRoot(document.getElementById("root"));root.render( <StrictMode> <App /> </StrictMode>,);import { createRoot } from "react-dom/client";import App from "./App.jsx";import "./watchDom.js"; const root = createRoot(document.getElementById("root"));root.render(<App />);Without StrictMode, React calls App once per render, as a production build does. One click prints Render App with quantity 2 once, and the DOM: line is the same as before: StrictMode never changed the commit.
This is the read-the-page example, starting at 5. You click Add one twice. On the second click, what does the log say the page shows?
import { useState } from "react";
export default function App() {
const [quantity, setQuantity] = useState(5);
// Only for this experiment: read the page while React renders.
const onPage = document.querySelector("strong")?.textContent ?? "nothing yet";
console.log(`Rendering ${quantity}. The page shows: ${onPage}`);
return (
<p>
Green tea: <strong>{quantity}</strong>{" "}
<button onClick={() => setQuantity(quantity + 1)}>Add one</button>
</p>
);
}import { useState } from "react"; export default function App() { const [quantity, setQuantity] = useState(5); // Only for this experiment: read the page while React renders. const onPage = document.querySelector("strong")?.textContent ?? "nothing yet"; console.log(`Rendering ${quantity}. The page shows: ${onPage}`); return ( <p> Green tea: <strong>{quantity}</strong>{" "} <button onClick={() => setQuantity(quantity + 1)}>Add one</button> </p> );}The second click's render works with 7, but the commit for 7 hasn't happened yet, so the page still shows 6: Rendering 7. The page shows: 6.
Each row repeats the order's total, so one click changes every row. Change what the component returns so a click changes less. After your change, how many DOM lines does one click on Add black tea print?
import { useState } from "react";import "./watchDom.js"; export default function Order() { const [teas, setTeas] = useState(["Green tea", "Mint tea"]); const total = teas.length * 4; return ( <div> <ul> {teas.map((tea, index) => ( <li key={index}>{tea}: $4 of ${total}</li> ))} </ul> <button onClick={() => setTeas([...teas, "Black tea"])}>Add black tea</button> </div> );}import { useState } from "react";import "./watchDom.js"; export default function Order() { const [teas, setTeas] = useState(["Green tea", "Mint tea"]); const total = teas.length * 4; return ( <div> <ul> {teas.map((tea, index) => ( <li key={index}>{tea}: $4</li> ))} </ul> <p>Total: ${total}</p> <button onClick={() => setTeas([...teas, "Black tea"])}>Add black tea</button> </div> );}Before, each click changed the total inside every row, plus the new row: 3 DOM changes with two teas, and more as the list grows. Now the rows never change, so a click makes 2 changes: the new <li> and the one total.
Check your understanding
For each question, find the trigger first, then follow the render and the commit. Press Edit & run on a question's code to check your answer in its console.
Question 1 of 8What are the two things that trigger a render?
Choose an answer to see the explanation.
Question 2 of 8You click Add one. Which components does React call?
Read the code, then predictimport { useState } from "react"; import "./watchDom.js"; function Header() { console.log("Render Header"); return <h3>Tea Corner</h3>; } function QuantityPicker({ quantity, onAdd }) { console.log("Render QuantityPicker"); return ( <p> Green tea: <strong>{quantity}</strong>{" "} <button onClick={onAdd}>Add one</button> </p> ); } function PriceTag({ total }) { console.log("Render PriceTag"); return <p>Total: ${total}</p>; } function OrderPanel() { const [quantity, setQuantity] = useState(1); console.log("Render OrderPanel"); return ( <div> <QuantityPicker quantity={quantity} onAdd={() => setQuantity(quantity + 1)} /> <PriceTag total={quantity * 4} /> </div> ); } export default function App() { console.log("Render App"); return ( <section> <Header /> <OrderPanel /> </section> ); }Choose an answer to see the explanation.
Question 3 of 8A click handler calls three set functions in a row. How many renders follow?
Choose an answer to see the explanation.
Question 4 of 8In React 19, a
setTimeoutcallback calls two set functions. How many renders follow?Choose an answer to see the explanation.
Question 5 of 8You click Add one once. During that render, what does the page show in the
<strong>?Read the code, then predictimport { useState } from "react"; export default function App() { const [quantity, setQuantity] = useState(1); // Only for this experiment: read the page while React renders. const onPage = document.querySelector("strong")?.textContent ?? "nothing yet"; console.log(`Rendering ${quantity}. The page shows: ${onPage}`); return ( <p> Green tea: <strong>{quantity}</strong>{" "} <button onClick={() => setQuantity(quantity + 1)}>Add one</button> </p> ); }Choose an answer to see the explanation.
Question 6 of 8A component renders again and returns exactly the same JSX as last time. What does the commit do?
Choose an answer to see the explanation.
Question 7 of 8With StrictMode in development, one click changes a component's state. How many times does React call the component, and how many commits follow?
Choose an answer to see the explanation.
Question 8 of 8When does the browser paint the new count?
Choose an answer to see the explanation.
Key takeaways
- Every screen update is a trigger, a render, and a commit; then the browser paints.
- Two things trigger a render: the first
root.render, and set functions. Several updates in one event, timer, or promise are batched into one render. - Rendering is React calling your components, from the one whose state changed down through everything inside it. The page doesn't change during a render.
- StrictMode calls each component twice in development, and still commits once.
- The first commit adds the whole tree at once. Later commits change only the DOM nodes that differ from the last render, and nothing when nothing differs.
- The browser paints after your JavaScript finishes, so a commit's changes appear together.
Remember the one-liner.
A set function asks, React renders by calling your components, commits only the differences, and the browser paints.
Coming next: State as a snapshot. It shows why a set call doesn't change the variable in the code that's running, and what each render's values mean for your handlers and timers.