React useState Explained with Practical Examples

useState is the React Hook that lets a component remember values between renders. Learn how it works with practical examples.

5 min read

The useState Hook is how a React component remembers values between renders. It returns the current value and a setter function, and calling the setter requests a new render. Without it, a component forgets everything as soon as it returns.

What useState does

A plain variable cannot keep a value between renders. React runs the component again on every render, so the variable is recreated each time, and changing it never tells React to update the screen.

State fixes both problems. The Hook returns an array with two items: the current value and the setter. The value survives re-renders, and the setter both records a change and triggers the next render.

App.jsxApp.jsx
import { useState } from "react";
 
const [count, setCount] = useState(0);

The zero is the initial value, and it is used only on the first render. After this line, the first variable holds zero and the second is the function you call to change it. On later renders, React ignores the argument and returns the value it already stored.

Adding state to a component

A counter is the smallest complete example. The button reads the current count, and the handler asks for the next one.

App.jsxApp.jsx
import { useState } from "react";
 
function Counter() {
  const [count, setCount] = useState(0);
  function handleClick() {
    setCount(count + 1);
  }
  return <button onClick={handleClick}>Clicked {count} times</button>;
}

Click the button and the text changes from "Clicked 0 times" to "Clicked 1 times", then 2, and so on. Each click records the next value, and the next render returns that value from the Hook.

Two ways to set the next value

The setter accepts a plain value or a function. Pass a value when the next state does not depend on the current one, such as resetting a counter to zero.

Pass a function when the next value is calculated from the current one:

App.jsxApp.jsx
setCount((previous) => previous + 1);

React treats this function as an updater. It queues the function and, during the next render, calls it with the latest pending value. Updating state based on the previous state explains why this matters when you set a value more than once.

By convention, the argument is named after the first letter of the variable, like previous for count, but any clear name works.

Reading the variable right after the setter still gives the old value. The update applies to the next render, not the line that is still running. Why state does not update immediately shows the snapshot behind that.

Rules to follow

  • Call the Hook at the top level of the component, never inside a loop or condition.
  • Treat state as read-only. Replace values instead of mutating them.
  • Keep the initializer and updater pure, with no side effects inside them.

In development, Strict Mode may call the initializer and updater twice to expose accidental side effects. That is exactly why they must stay pure.

State is also private to its component. Two copies of the same component keep separate values, and a parent cannot change a child's state directly. That privacy is the main difference between props and state.

When you do not need state

Not every value belongs in state. Anything you can compute from existing props or state during render should stay a plain calculation.

  • A derived value: compute it during render.
  • A value used only inside one handler: use a plain variable.
  • Data passed from a parent: read it as a prop.

Storing a derived value creates two sources of truth that can drift apart. Compute first, and reach for state only when the UI must remember something across renders.

A full name shown on screen is a derived value. Combine the first and last name during render instead of saving the joined string in state.

What to learn next

Practice with objects and arrays next, the two shapes people update most often in state. From there, adding TypeScript keeps invalid updates from ever reaching the screen.

Rune AI

Rune AI

Key Insights

  • useState returns the current value and a setter function.
  • The setter updates state for the next render, not the current one.
  • Pass a function to the setter when the next value depends on the previous value.
  • Call Hooks only at the top level of a component.
  • Do not store values that can be derived during render.
RunePowered by Rune AI

Frequently Asked Questions

What does useState return?

It returns an array with exactly two items. The first is the current state value, and the second is a setter function that updates the value and triggers a re-render.

Why does a component need state instead of a normal variable?

A normal variable is reset on every render, and changing it does not tell React to render again. useState retains the value between renders and triggers a new render when the setter runs.

Can I call useState inside a condition?

No. Hooks must be called at the top level of a component in the same order on every render. Move the condition inside the component body instead.

Conclusion

useState gives a component two things: a value that survives re-renders and a setter that requests the next render. Use it for data the UI must remember, and keep every update immutable.