Remember
useState stores information between renders.
State gives a component memory. Props bring data from a parent, while each render receives a fixed snapshot of both props and state.
React calls a component to calculate its interface. Props are inputs to that call, and state is React-managed memory associated with the component's position in the UI tree.
useState stores information between renders.
Props let a parent configure a child.
Each render sees fixed values for that moment.
Call useState at the top level of a component. It returns an array containing the current state value and a setter function. Calling the setter requests a new render with the updated value.
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
useState(0) sets the initial value.React uses 0 on the first render. The initial value is ignored on later renders.
count is the value for this render.Read it when calculating JSX or handling an interaction.
setCount requests an update.Do not assign to count directly. A setter tells React to render again and keeps the interface in sync.
function SearchBox() {
const [query, setQuery] = useState("");
return (
<label>
Search
<input
value={query}
onChange={event => setQuery(event.target.value)}
/>
<span>Looking for: {query || "nothing yet"}</span>
</label>
);
}
Hook rule: call useState only at the top level of a component or custom Hook, not inside conditions, loops, or nested functions. A stable call order lets React match each state value to the correct Hook.
Both affect what a component renders, but ownership is different. A parent owns the props it passes. The component owns its local state.
function ExpandableNote({ title }) {
// title is a prop; the parent owns it.
const [isOpen, setIsOpen] = useState(false);
return (
<section>
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? "Hide" : "Show"} {title}
</button>
{isOpen && <p>State belongs to this component instance.</p>}
</section>
);
}
export default function App() {
return <ExpandableNote title="React state" />;
}
Examples include a label, product data, or an event handler.
Examples include selected tabs, typed text, or whether a panel is open.
The two values can drift apart. Read the prop directly when it already represents the value you need.
Calling a setter does not change the state variable in code that is already running. It asks React for another render. React then calls the component again and gives that render a new state value.
score value.setScore asks React to render with a new value.score.function ScoreButton() {
const [score, setScore] = useState(0);
function handleClick() {
setScore(score + 1);
alert(score); // Shows the value from this render.
}
return (
<button onClick={handleClick}>Score: {score}</button>
);
}
On the first click, score is 0 throughout that handler, so the alert shows 0. After the handler finishes, React renders again with score equal to 1. The old handler cannot see forward into the next render.
count.function addThree() {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// If count is 0, each line requests 1, not 1, then 2, then 3.
}
Boundary for the next topic: when a new value depends on the previous queued value, React provides functional updates such as setCount(current => current + 1). Update queues and batching explain why that form can apply repeated changes correctly.
Keep this model: props are read-only inputs from a parent, state is local memory managed through a setter, and every render works with its own fixed snapshot.
count = count + 1 not correctly update the interface?
Changing the local variable does not ask React to render again or update React's stored state. Call setCount(count + 1) instead.
theme="dark" to a child. Is theme a prop or state in the child?
It is a prop because the parent supplies and owns it. The child reads it but does not change it directly.
setScore(score + 1), why does the next line still see the old score?
The running event handler belongs to the current render and keeps that render's snapshot. The setter requests a new render; it does not rewrite the value inside code already running.