# State

Course: [React](https://www.devtutorials.ai/learn/react) · Section: Data Flow in React
Source: https://www.devtutorials.ai/learn/react/data-flow/state

> Local component state with useState: state vs props, reading and updating state, state-driven rendering, updating from the previous value, common state values, immutable updates of objects and arrays, and when to use state.

## Introduction to Local Component State

**State** is data that a component remembers and can change. When state changes, React shows the new value on the screen.

It is called **local** state because it belongs to one component. Other components cannot see it unless you send it to them as props.

### Real-World Example

In a bookshop app, the cart button shows "Cart (0)". When the user clicks **Add to cart**, the component calls `setCount(count + 1)`.

React stores the new count and renders the component again, so the button shows "Cart (1)". The developer never changes the page text directly.

State is data that a component remembers. When state changes, React updates the screen.

## State Compared with Props

Props and state are both data, but they come from different places.

<div className="tscroll">

| | Props | State |
|---|---|---|
| Where it comes from | Sent by the parent | Created inside the component |
| Who can change it | Only the parent | The component itself |
| Can the component change it? | No, it is read-only | Yes, with its set function |
| What happens on change | The child shows the new props | The component shows the new state |

</div>

The two often work together. A parent keeps a value in its state and sends it to a child as a prop.

      <button onClick={() => setCount(count + 1)}>Add a book</button>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

`App` owns the state `count`. It sends `count` to `CartBadge` as a prop. When you click, `App` changes its state, and `CartBadge` gets the new number.

## Creating State with `useState`

To create state, you use a function from React called `useState`.

First, import it at the top of your file:

```tsx
import { useState } from "react";
```

Then, inside your component, write this line:

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

This line creates one piece of state. It has three parts:

<figure className="react-fig">
<svg viewBox="0 0 520 150" role="img" aria-labelledby="fig-usestate-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-usestate-title">The three parts of const [count, setCount] = useState(0): count is the value, setCount changes it, 0 is the starting value</title>
  <text x="260" y="40" textAnchor="middle" style={{ fill: "var(--ink)", fontSize: 20, fontFamily: "var(--mono)" }}>
    <tspan>const [</tspan><tspan style={{ fill: "var(--accent)", fontWeight: 700 }}>count</tspan><tspan>, </tspan><tspan style={{ fill: "var(--accent-secondary)", fontWeight: 700 }}>setCount</tspan><tspan>] = useState(</tspan><tspan style={{ fill: "var(--ok)", fontWeight: 700 }}>0</tspan><tspan>);</tspan>
  </text>
  <g style={{ strokeWidth: 1.5, fill: "none" }}>
    <path d="M146 50 L90 100" style={{ stroke: "var(--accent)" }} />
    <path d="M248 50 L260 100" style={{ stroke: "var(--accent-secondary)" }} />
    <path d="M458 50 L430 100" style={{ stroke: "var(--ok)" }} />
  </g>
  <g style={{ fontFamily: "var(--sans)", fontSize: 13, fontWeight: 600 }}>
    <text x="90" y="118" textAnchor="middle" style={{ fill: "var(--accent)" }}>The value now</text>
    <text x="260" y="118" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>The function to change it</text>
    <text x="430" y="118" textAnchor="middle" style={{ fill: "var(--ok)" }}>The starting value</text>
  </g>
</svg>
<figcaption>One line, three parts.</figcaption>
</figure>

- **`count`** holds the value now. At the start, it is `0`.
- **`setCount`** is a function. You call it to change `count`.
- **`0`** is the starting value. It is used only the first time.

You can give the state any name you like. But there is a common naming pattern that most people follow:

- Give the value a simple name, like `count`.
- Give the function the same name with `set` at the start, like `setCount`.

### Typing State

Every piece of state has a type, such as a number or text. Most of the time, TypeScript finds the type by itself from the starting value:

```tsx
const [count, setCount] = useState(0);      // 0 is a number, so count is a number
const [name, setName] = useState("");       // "" is text, so name is text
const [isOpen, setIsOpen] = useState(false); // false is true/false, so isOpen is a boolean
```

Sometimes the starting value is not enough. For example, an empty list `[]` does not tell TypeScript what kind of items will go in it.

In that case, write the type yourself, inside `< >` after `useState`:

```tsx
const [books, setBooks] = useState<string[]>([]);
```

`<string[]>` means "a list of text". Now TypeScript knows that `books` holds text items, and it warns you if you try to add a number.

Call `useState` at the top of your component, not inside an `if` or a loop. React needs to find each piece of state in the same order every time.

## Reading State

To read state, use its variable, like any other variable. You can show it in TSX, use it in maths, or use it in a condition.

```tsx
<p>Books in cart: {count}</p>
<p>Total: ₹{count * 299}</p>
{count === 0 && <p>Your cart is empty.</p>}
```

## Updating State

To change state, call its set function with the new value. **Do not** change the variable directly.

<figure className="react-fig">
<svg viewBox="0 0 520 190" role="img" aria-labelledby="fig-state-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-state-title">The state update cycle: click, setCount, React runs the component again, the screen shows the new value</title>
  <path id="state-loop" d="M130 50 L390 50 L390 140 L130 140 Z" style={{ fill: "none", stroke: "var(--faint)", strokeWidth: 2, strokeDasharray: "6 5" }} />
  <g style={{ fontFamily: "var(--sans)", fontSize: 13, fontWeight: 600 }}>
    <rect x="60" y="30" width="140" height="40" rx="8" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)" }} />
    <text x="130" y="55" textAnchor="middle" style={{ fill: "var(--accent)" }}>1. You click</text>
    <rect x="320" y="30" width="140" height="40" rx="8" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)" }} />
    <text x="390" y="55" textAnchor="middle" style={{ fill: "var(--accent)" }}>2. setCount(1)</text>
    <rect x="320" y="120" width="140" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)" }} />
    <text x="390" y="145" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>3. React re-renders</text>
    <rect x="60" y="120" width="140" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)" }} />
    <text x="130" y="145" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>4. Screen shows 1</text>
  </g>
  <circle r="7" style={{ fill: "var(--accent)" }}>
    <animateMotion dur="4s" repeatCount="indefinite" path="M200 50 L320 50 M390 70 L390 120 M320 140 L200 140 M130 120 L130 70" />
  </circle>
</svg>
<figcaption>Every state update goes round this cycle. "Re-render" means React runs the component again.</figcaption>
</figure>

When you call `setCount(1)`, React does three things:

1. It saves the new value.
2. It runs your component function again. This is called a **re-render**.
3. It updates only the parts of the screen that changed.

      <h2>Copies: {count}</h2>
      <button onClick={() => setCount(count + 1)}>+1</button>{" "}
      <button onClick={() => setCount(count - 1)}>-1</button>{" "}
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

### Why Not Change the Variable Directly?

If you write `count = count + 1`, the number changes in memory, but React does not know. It does not re-render, so the screen stays the same. Only the set function tells React to update the screen.

## State-Driven Rendering

**State-driven rendering** means the screen is decided by the state. You do not change the page yourself. You change the state, and React shows the matching screen.

      <h3>Atomic Habits</h3>
      <button onClick={() => setShowDetails(!showDetails)}>
        {showDetails ? "Hide details" : "Show details"}
      </button>

      {showDetails && (
        <p>by James Clear · 320 pages · ₹499</p>
      )}
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 4px 12px 12px; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

`showDetails` controls two things: the button text and the details paragraph. `!showDetails` flips `true` to `false`, and `false` to `true`.

## Updating State from Previous State

The set function does not change the state at once. React waits until your click code has finished, and then re-renders. So inside one click, `count` still holds the old value.

This causes a problem when you update the same state more than once. Try both buttons below.

      <h2>Count: {count}</h2>
      <button onClick={addThreeWrong}>+3 (wrong)</button>{" "}
      <button onClick={addThreeRight}>+3 (right)</button>{" "}
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

"+3 (wrong)" adds only 1. "+3 (right)" adds 3.

The right way passes a **function** to `setCount`. React gives that function the newest value as `prev`, and the function returns the next value. So each line builds on the line before it.

When the new value depends on the old value, write `setCount((prev) => prev + 1)`.

## Common State Values

State can hold any kind of value. These are the most common ones:

<div className="tscroll">

| Kind of value | Example | Used for |
|---|---|---|
| Number | `useState(0)` | Counters, quantities, scores |
| Text | `useState("")` | What the user types in a box |
| True or false | `useState(false)` | Show or hide, open or closed |
| Array | `useState<string[]>([])` | A list of items, such as a cart |
| Object | `useState({ name: "", email: "" })` | A group of related values, such as a form |

</div>

      <input
        placeholder="Type your name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <p>{name === "" ? "Hello, guest!" : "Hello, " + name + "!"}</p>
      <p>Letters typed: {name.length}</p>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Type in the box. Each key press calls `setName` with the new text, and the greeting updates at once. `e.target.value` is the text that is in the box now.

## Updating Objects and Arrays Immutably

**Immutable** means "not changed". In React, you never change an object or array that is in state. You make a **new copy** with the change, and pass the copy to the set function.

React compares the old value and the new value. If you change the old one and pass it back, React sees the same object and does not update the screen.

<figure className="react-fig ngc-fig">
<svg viewBox="0 0 520 200" role="img" aria-labelledby="fig-copy-title" style={{ width: "100%", maxWidth: 560, height: "auto" }}>
  <title id="fig-copy-title">Animation: the old array stays the same; its items are copied into a new array, the new item is added, and the new array is passed to setBooks</title>
  <defs><marker id="st-a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 L10 5 L0 10 z" style={{ fill: "var(--ngc-purple)" }} /></marker></defs>
  <text x="110" y="22" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Old array: books</text><text x="410" y="22" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>New array: [...books, new]</text>
  <rect x="20" y="36" width="180" height="32" rx="8" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 1.5 }} /><text x="110" y="56" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>The Alchemist</text><rect x="20" y="76" width="180" height="32" rx="8" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 1.5 }} /><text x="110" y="96" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Wings of Fire</text>
  <rect x="320" y="36" width="180" height="112" rx="10" style={{ fill: "none", stroke: "var(--ngc-purple)", strokeWidth: 1.5, strokeDasharray: "6 4" }} />
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.05;0.09;0.93;1" dur="9s" repeatCount="indefinite" /><line x1="205" y1="125" x2="312" y2="125" style={{ stroke: "var(--ngc-purple)", strokeWidth: 2 }} markerEnd="url(#st-a)" /><text x="258" y="117" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 11, fontWeight: 700, fontFamily: "var(--sans)" }}>...books</text></g>
  <g opacity="0"><rect x="20" y="36" width="180" height="32" rx="8" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }} /><text x="110" y="56" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>The Alchemist</text><animateTransform attributeName="transform" type="translate" values="0 0;0 0;300 0;300 0" keyTimes="0;0.1;0.3;1" dur="9s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.1;0.11;0.93;1" dur="9s" repeatCount="indefinite" /></g>
  <g opacity="0"><rect x="20" y="76" width="180" height="32" rx="8" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }} /><text x="110" y="96" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Wings of Fire</text><animateTransform attributeName="transform" type="translate" values="0 0;0 0;300 0;300 0" keyTimes="0;0.25;0.45;1" dur="9s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.25;0.26;0.93;1" dur="9s" repeatCount="indefinite" /></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.5;0.54;0.93;1" dur="9s" repeatCount="indefinite" /><rect x="320" y="116" width="180" height="32" rx="8" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }} /><text x="410" y="136" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>+ Atomic Habits (new)</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.55;0.59;0.93;1" dur="9s" repeatCount="indefinite" /><text x="110" y="135" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>✓ not changed</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.68;0.72;0.93;1" dur="9s" repeatCount="indefinite" /><rect x="330" y="162" width="160" height="30" rx="8" style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)", strokeWidth: 1.5 }} /><text x="410" y="181" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 12, fontWeight: 700, fontFamily: "var(--mono)" }}>setBooks(newArray)</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.72;0.76;0.93;1" dur="9s" repeatCount="indefinite" /><text x="160" y="182" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 11, fontWeight: 700, fontFamily: "var(--sans)" }}>React sees a new array and updates the screen</text></g>
</svg>
<figcaption>Make a new array with the old items plus the new one. The old array is left as it was.</figcaption>
</figure>

### The Spread Operator

The **spread operator** `...` copies all the items of an array, or all the fields of an object, into a new one.

```tsx
// Arrays
setBooks([...books, "Atomic Habits"]);                // add an item
setBooks(books.filter((b) => b !== "Wings of Fire")); // remove an item

// Objects
setReader({ ...reader, city: "Chennai" });            // change one field
```

- `[...books, "Atomic Habits"]` makes a new array with all old books plus the new one.
- `filter` makes a new array without the removed item.
- `{ ...reader, city: "Chennai" }` makes a new object with all old fields, then changes `city`.

Now try the wrong way and the right way side by side.

      <ul>
        {books.map((book, i) => <li key={i}>{book}</li>)}
      </ul>
      <button onClick={addWrong}>Add (wrong)</button>{" "}
      <button onClick={addRight}>Add (right)</button>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Press "Add (wrong)". The console says the array grew, but the list on the screen did not change. Now press "Add (right)". The list updates, and it also shows the books that the wrong button added.

### Updating an Object

      <p>{reader.name} from {reader.city} has {reader.points} points.</p>
      <button onClick={() => setReader({ ...reader, city: "Chennai" })}>Move to Chennai</button>{" "}
      <button onClick={() => setReader({ ...reader, points: reader.points + 10 })}>+10 points</button>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Each button makes a new object. `...reader` copies `name`, `city` and `points`. The field written after it replaces the old value.

## When to Use State

Ask one question: **does this value change while the app is running, and must the screen show the change?** If yes, use state.

- **Use state** for things the user changes: a counter, typed text, an open or closed menu, the items in a cart.
- **Do not use state** for values that never change, such as a shop name. Use a normal variable.
- **Do not use state** for values you can work out from other state. If you have `books`, the count is just `books.length`. You do not need a second state for it.
- **Do not use state** for data that comes from the parent. Use props.

## Local State Best Practices

- **Keep state as small as you can.** Store only what you need, and work out the rest.
- **Keep state in the component that uses it.** If two components need the same state, keep it in their shared parent and pass it down as props.
- **Give clear names.** Use `isOpen` and `setIsOpen`, not `x` and `setX`.
- **Use the function form** `setCount((prev) => prev + 1)` when the new value depends on the old one.
- **Always make copies** of objects and arrays. Never use `push` or change a field directly.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Changing state directly: `count = count + 1` | The screen does not change | Use the set function: `setCount(count + 1)` |
| Using `push` on an array in state | The screen does not change | Make a new array: `setBooks([...books, item])` |
| Changing an object field: `reader.city = "Chennai"` | The screen does not change | Make a new object: `setReader({ ...reader, city: "Chennai" })` |
| Calling `setCount(count + 1)` several times in one click | The value goes up by only 1 | Use `setCount((prev) => prev + 1)` |
| Calling `useState` inside an `if` or a loop | React shows an error | Call `useState` at the top of the component |
| Reading state right after setting it | You get the old value | Use the new value directly, or read it on the next render |
| Calling the set function during render: `{setCount(1)}` | `Too many re-renders` error | Call set functions only in events, such as `onClick` |
| Forgetting to import `useState` | `useState is not defined` | Add `import { useState } from "react";` |

</div>

## Check Yourself

## Exercises

## What to Remember

- State is data a component remembers. Create it with `useState`, and change it only with its set function.
- When state changes, React re-renders and the screen shows the new value.
- Use `prev => ...` when the new value depends on the old one, and always copy objects and arrays.

Next: Event Handling
