State and Hooks
Props flow in from a parent; state is data a component owns and manages itself, and — critically — updating it triggers React to automatically re-render that component with the new value.
useState
import { useState } from "react";nnfunction LessonCounter() {n const [completed, setCompleted] = useState(0);nn return (n <div>n <p>Completed: {completed}</p>n <button onClick={() => setCompleted(completed + 1)}>n Mark lesson donen </button>n </div>n );n}
useState(0) declares a piece of state starting at 0, and returns a pair: the current value (completed) and a function to update it (setCompleted). Calling setCompleted is what actually triggers a re-render — directly reassigning completed would not.
The Rules of Hooks
useState is called a Hook — a special React function, always starting with use, that only works inside a component (or inside another Hook). Hooks must always be called in the exact same order on every render, which means they can never be placed inside an if statement or a loop — this is a strict rule, and React’s tooling will warn you loudly if you break it.
useEffect — running code in response to changes
import { useState, useEffect } from "react";nnfunction LessonTimer() {n const [seconds, setSeconds] = useState(0);nn useEffect(() => {n const interval = setInterval(() => setSeconds(s => s + 1), 1000);n return () => clearInterval(interval); // cleanup, runs when the component unmountsn }, []); // empty array: run once, when the component first appearsnn return <p>{seconds} seconds elapsed</p>;n}
useEffect runs side effects — things outside of just rendering markup, like timers, subscriptions, or fetching data (covered in the next lesson). The array at the end (the “dependency array”) controls when it re-runs; an empty array means “only once, when this component first appears.”