Topic 04 / 14

State with useState

~11 min read  //  React Series  //  Coding India

State = Memory That Triggers Renders

Regular variables reset on every render and don’t update the screen. State persists between renders, and setting it re-renders the component:

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);   // [value, setter], 0 = initial

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

Click → setCount → React re-runs Counter with the new value → the DOM updates. That’s the entire reactive loop.

The Rules of Hooks

  • Call hooks only at the top level of a component — never inside ifs, loops, or nested functions.
  • Call them only from components or custom hooks.

React tracks hooks by call order; conditions would scramble it.

State Updates Are Asynchronous Snapshots

function handleClick() {
  setCount(count + 1);
  setCount(count + 1);     // still the OLD count — both set the same value!
}

// updater function — receives the latest value
function handleClick() {
  setCount(c => c + 1);
  setCount(c => c + 1);    // now +2 works
}

Rule: when the new state depends on the old, pass a function.

Objects & Arrays — Always Replace, Never Mutate

React detects changes by comparing references. Mutating the existing object looks like “no change”:

const [user, setUser] = useState({ name: "Ravi", score: 0 });

// WRONG — same object, React may not re-render
user.score = 10;
setUser(user);

// RIGHT — new object via spread
setUser({ ...user, score: 10 });
const [todos, setTodos] = useState([]);

setTodos([...todos, newTodo]);                          // add
setTodos(todos.filter(t => t.id !== id));               // remove
setTodos(todos.map(t =>
  t.id === id ? { ...t, done: !t.done } : t            // update one
));

These three array patterns are half of all React state code you’ll ever write.

Multiple State Variables

const [query, setQuery] = useState("");
const [page, setPage] = useState(1);
const [isOpen, setIsOpen] = useState(false);

Split unrelated state; group fields that change together into one object.

Lifting State Up

When two components need the same data, move the state to their closest common parent and pass it down:

function App() {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBox query={query} onChange={setQuery} />
      <ResultList query={query} />
    </>
  );
}

The child calls onChange(newValue); the parent owns the truth. State down, events up — the fundamental React data flow.