State and useState — the Value a Component Remembers on Its Own
A look at React's State in contrast to Props. Why a plain variable won't do, why you receive useState as a const, and why you must always change the value through the setter.
Last time we said Props are values received from outside (the parent) that can’t be changed. And it left us with a natural question: how do we handle a value the component holds itself and that changes over time? The answer is today’s subject — State.
A counter that goes up each time you click, the characters typed into an input, a menu that opens and closes — every value that changes as the user interacts is State.
Why won’t a plain variable do?
“If it’s just a changing value, can’t I use a regular variable?” you’d think. But inside a component, a plain variable fails at two things.
function Counter() {
let count = 0;
return <button onClick={() => { count += 1; }}>{count}</button>;
}
No matter how you click this button, the number on screen doesn’t budge from 0. For two reasons:
- When it changes, the screen isn’t redrawn. In Part 1 we said React “redraws the screen when state changes” — but a plain variable isn’t watched by React, so it never knows it changed.
- If it were redrawn, the value would reset. Even if it somehow redrew, the component function runs from the top again, so
let count = 0runs again and returns to 0.
So a component needs a special kind of variable that remembers its value across re-renders and redraws the screen when that value changes. That’s State, and the tool that makes it is useState.
How to use useState
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
useState takes an initial value and returns an array of length 2, which you receive by destructuring:
- first (
count): the current state value - second (
setCount): a dedicated function (setter) that changes it
The names are up to you, but by convention you pair them as x, setX. Now clicking the button calls setCount, which changes the state, and the component redraws accordingly, showing the new number.
These special functions that start with
useare called Hooks. Think of it as a function component “hooking into” React features like State and side effects. A good part of this series is a story about these hooks.
Why receive it as const, not let?
Receiving a changing value as const looks odd. “It’s going to change, shouldn’t it be let?” But hidden here is a misunderstanding about how React renders.
The key is this: count never changes within a single render. State changing doesn’t mean editing that value in place — it means running the component function again from the top. In that new run, useState returns the updated value this time, and const count is freshly bound to that new value.
So the flow isn’t “keep editing one count” but “a fresh count is created for each render.” Within each render, count is a fixed snapshot, so const is exactly the right description. In fact, if you made it let and edited count = ... directly, React would have no idea about the change and would overwrite it on the next render — pure confusion.
Always change the value through the setter
The single most important practical rule follows from that: never edit state directly; always change it through the setter.
count = count + 1; // ❌ React doesn't know → screen doesn't change
setCount(count + 1); // ✅ change state + schedule a re-render
setCount doesn’t just change the value — it schedules with React: “redraw this component.” That one line binds value-change and screen-update into one, which is the very reason State exists.
The same goes for object or array state. Don’t edit the insides (user.name = ...); make a new object and pass it to the setter.
setUser({ ...user, name: 'New name' });
When basing it on the previous value, pass a function
One practical tip. When deciding the next value based on the previous state, it’s safer to pass a function instead of a value.
setCount(prev => prev + 1);
Give setCount a function and React feeds in the latest state as prev. Even when state changes pile up — say you click a button rapidly several times — the values don’t get tangled, preventing bugs like “why did it only go up once?”
To sum up, State is a value that persists across re-renders and redraws the screen when it changes, and you always change it through the setter. By now the flow of “when state changes, the screen redraws” should feel concrete.
But what if, when state changes, you want to do something other than draw the screen? Say you need to send a request to the server every time the search term (state) changes. The tool for that “work outside the screen” is the next article’s subject — useEffect.