Remember the todo list where every change to the data needed a hand-written change to the DOM? React's idea is to delete that second half. You write a function that says: given this data, the page looks like this. When the data changes, React calls your function again and works out the difference itself.
Move the slider. You never wrote an update. The output is just the function, re-run.
UI = f(state)
function Cart({ count }) { return ( <div> <h2>{count} {count === 1 ? "item" : "items"}</h2> {count === 0 && <p>Your cart is empty.</p>} {count > 9 && <p className="warn">Free shipping!</p>} </div> ); }
That's it. <Greeting name="Ada" /> is a call: Greeting({ name: "Ada" }). The parent decides the arguments. The child can read them but not change them. Data flows down.
Edit the parent's input. The child re-renders with the new prop. It has no idea where the value came from and doesn't need to.
<Greeting name={name} />A component is a function, and React calls it again on every render. Locals inside a function are born and die with each call.
So: what happens when you click a counter written with let count = 0; count++ instead of useState?
Never changes. The variable increments, but nothing tells React to re-render. And if something did, the function would run again and let count = 0 would reset it. useState solves both: it stores the value outside the function, and setting it schedules a re-render.
let count = 0; <button onClick={() => count++}> {count} </button>
const [count, setCount] = useState(0); <button onClick={() => setCount(count + 1)}> {count} </button>
List sets state, which components re-render?Setting state re-runs the component that owns it. But what about its parent? Its siblings? Its children?
List and everything under it. Re-rendering flows down, never up or sideways. Header is untouched. Every Item re-runs even if its props didn't change (that's what memo is for, later). This one rule explains most "why is this slow" and "why didn't this update" questions.
Highlighted = re-rendered.
Your function returns a description: a tree of plain objects (the "virtual DOM"). React keeps the previous description, compares the two, and touches the real DOM only where they differ.
So re-rendering a component is cheap; it's building a description. Touching the DOM is the expensive part, and React does the minimum of it.
DOM operations sent: 0
The tags in your components aren't HTML. They're a shorthand that the bundler rewrites into calls before the browser ever sees them. That's why you can put a JavaScript expression inside { }: you were always in JavaScript.
It also explains the quirks: className instead of class (because class is a reserved word), and why a component must start with a capital letter (lowercase means a built-in tag).
<Button size="lg" onClick={save}> Save {count} items </Button>
jsx(Button, { size: "lg", onClick: save, children: ["Save ", count, " items"] })
Rendering should only describe. But real apps need to fetch data, set a document title, subscribe to a socket, start a timer. Those are effects: things that happen after the render, outside React's description of the page.
useEffect(fn, deps): run fn after render, and again only when something in deps changed. The rules around this are where most React confusion lives, and they turn out to have a simple mechanical reason.
function Profile({ id }) { const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/users/${id}`) .then(r => r.json()) .then(setUser); }, [id]); // re-run only when id changes if (!user) return <p>Loading…</p>; return <h1>{user.name}</h1>; }
Nor what a server is, nor what to send a Google crawler, nor how to load data before the first paint. Plain React ships an empty <div> and a script, and the script does everything. That's fine for a dashboard and terrible for a blog.