State with useState
Props flow into a component from the outside, but a component often needs to track its own data that changes over time — a counter's current count, whether a menu is open, what someone has typed into a field. That's what state is for, and useState is how you add it to a function component.
Declaring state
useState(initialValue) returns a pair: the current value, and a function to update it. The square-bracket syntax is array destructuring, naming both parts at once:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
"Count: 0" and a "+1" button. Each click re-renders the component with count increased by one: "Count: 1", "Count: 2", and so on.
count is the current value (starting at 0, the argument passed to useState). setCount is the only correct way to change it — calling setCount(count + 1) tells React "re-render this component, and this time count should be this new value." Writing count = count + 1 directly would not work; React has no way to know the value changed.
State is private to each component instance
Every time you render a component, its state is independent from any other instance of the same component:
function App() {
return (
<>
<Counter />
<Counter />
</>
);
}
Two separate counters, each starting at 0. Clicking the first one's button only changes that counter — the second stays at whatever it was.
setCount doesn't change count immediately in the same line of code that called it. setCount(count + 1); console.log(count); logs the old value, because count in that scope was already fixed when the component last rendered. The new value only shows up on the next render.