Q&A
React Hooks introduction - useState
Overview
Before Hooks, only class components could have state. Hooks let function components "hook into" React features. useState is the entry point: it gives a component a piece of state that persists across re-renders and, when changed, causes React to re-render the component with the new value.
graph TD
A[Component renders] --> B["const [count, setCount] = useState(0)"]
B --> C[User clicks button]
C --> D["setCount(count + 1)"]
D --> E[React schedules re-render]
E --> A
style D fill:#1e1b4b,color:#fff
Basic Usage
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0); // initial value 0
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
useState(0)returns a pair: the current state (count) and a setter (setCount).- Calling
setCount(...)updates state and re-renders the component. - The destructuring names are yours to choose (
[count, setCount]is convention).
Functional Updates (when new state depends on old)
If the next state depends on the previous state - especially with multiple rapid updates - pass a function to the setter so you always read the latest value:
setCount(prev => prev + 1); // safe; uses the most recent state
This avoids stale-state bugs that arise because state updates are asynchronous and may be batched.
State with Objects and Arrays
State updates replace, they don't merge. With objects/arrays, create a new copy (immutability):
const [user, setUser] = useState({ name: "Ada", age: 30 });
setUser(prev => ({ ...prev, age: 31 })); // spread to keep other fields
const [items, setItems] = useState([]);
setItems(prev => [...prev, newItem]); // new array, don't mutate
Mutating state directly (user.age = 31) won't trigger a re-render and breaks React's model.
Lazy Initialization
If the initial value is expensive to compute, pass a function - React calls it only on the first render:
const [data, setData] = useState(() => expensiveComputation());
Rules of Hooks
- Only call Hooks at the top level - never inside loops, conditions, or nested functions. React relies on call order to track state across renders.
- Only call Hooks from React function components or custom Hooks - not regular functions.
// WRONG - conditional Hook
if (loggedIn) { const [x, setX] = useState(0); }
// RIGHT - top level, then branch on the value
const [x, setX] = useState(0);
if (loggedIn) { /* use x */ }
Multiple State Variables
Use useState as many times as you need; keep unrelated state separate:
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [open, setOpen] = useState(false);
Why This Matters in Interviews
React interviews almost always cover useState. Know that it returns a [value, setter] pair, that updates are asynchronous/batched (hence functional updates), that state must be treated immutably, and the Rules of Hooks (top-level only, and why - React tracks Hooks by call order). Distinguishing setCount(count+1) from setCount(c => c+1) is a classic discriminator.
Common Pitfalls
- Mutating state directly instead of creating a new object/array → no re-render.
- Relying on
countright aftersetCount(state hasn't updated yet - it's async). - Calling Hooks conditionally, breaking call-order tracking.
- Expecting object state to merge (it replaces; spread the old values).
References
- React
useStatereference: https://react.dev/reference/react/useState - Rules of Hooks: https://react.dev/reference/rules/rules-of-hooks