React: Build Modern Web Apps
Chapter 3 / 7· 22 min read· 0 cards

State and the useState Hook

Make components interactive — store changing data with useState and watch React re-render.

Making components remember and change

So far our components display fixed data passed in through props. But real apps are interactive — a counter goes up when clicked, a form fills as you type, a menu opens and closes. For this, a component needs its own state: data that can change over time and that the component "remembers" between renders. State is what brings React components to life, and you manage it with a hook called useState.

The useState hook

A "hook" is a special React function that adds capabilities to your component. useState is the most important one — it gives your component a piece of state. Here's the classic counter:

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);   // declare state, starting at 0

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Add One</button>
    </div>
  );
}

The line const [count, setCount] = useState(0) is the key. It does three things: creates a state variable count starting at 0, gives you a function setCount to change it, and uses array destructuring to name them. The convention is always [thing, setThing]. To read the state, use count; to change it, call setCount.


The magic: re-rendering

Here's the part that makes React so powerful, and it's worth understanding deeply. When you call setCount, React doesn't just change the variable — it re-renders the component, running the function again with the new value, and updates the screen to match. You never touch the DOM yourself; you just change the state, and React syncs the UI automatically:

// When the button is clicked:
// 1. setCount(count + 1) runs -> React updates the state
// 2. React RE-RENDERS the Counter component with the new count
// 3. The screen updates to show the new number — automatically!

// You declare WHAT the UI shows for a given state.
// React handles updating the screen. This is "declarative" in action.

This is the declarative model from chapter one, made concrete. You don't write "find the count element and change its text". You change the state, and React figures out the screen update. This automatic syncing of UI to state is the heart of how React works, and once it clicks, building interactive features becomes wonderfully simple.


The golden rule: never change state directly

A critical rule that catches every beginner: you must always update state using its setter function, never by changing the variable directly. If you change it directly, React doesn't know anything happened and won't re-render:

const [count, setCount] = useState(0);

// ❌ WRONG — React doesn't notice, screen won't update
count = count + 1;

// ✅ CORRECT — tells React to update state and re-render
setCount(count + 1);

Always go through the setter (setCount). It's how you signal to React "the state changed, please update the screen". Forgetting this — trying to mutate state directly — is the number one beginner mistake, and now you know to avoid it.


State can be any type

State isn't limited to numbers — it can hold strings, booleans, objects, arrays, anything. Here's a toggle using boolean state, a very common pattern:

import { useState } from "react";

function Toggle() {
  const [isOn, setIsOn] = useState(false);

  return (
    <div>
      <p>The light is {isOn ? "ON" : "OFF"}</p>
      <button onClick={() => setIsOn(!isOn)}>
        Toggle
      </button>
    </div>
  );
}

Clicking the button flips isOn between true and false, and the displayed text updates automatically. This toggle pattern — boolean state flipped on a click — powers menus, modals, dark mode, expandable sections, and countless other features. The same simple idea, used everywhere.


Updating state based on previous state

One important subtlety: when your new state depends on the old state, use the "functional update" form, which guarantees you're working with the latest value:

// Simple cases — fine
setCount(count + 1);

// When the update depends on the previous value, prefer this safer form:
setCount(prevCount => prevCount + 1);

// Especially important for multiple updates or async situations
function addThree() {
  setCount(c => c + 1);
  setCount(c => c + 1);
  setCount(c => c + 1);   // correctly adds 3
}

The functional form setCount(c => c + 1) passes you the guaranteed-current value, avoiding subtle bugs when multiple updates happen together. It's a good habit whenever your new state is calculated from the old. You now have the core of interactive React: components, props, and state. Next, we'll handle user input properly — events and forms.

Reading mode · scroll to read at your own pace

Finished "State and the useState Hook"?

Mark this chapter complete so you can pick up exactly where you left off. Your progress saves locally — sign in to sync across devices.

Was this chapter clear?

Try it yourself — open the Code Playground15+ languages — Python, JavaScript, Java, C++, SQL & more — full IDE-style editor, instant run. Your code is auto-saved per language.