useState
useState is a React Hook that lets a component declare and update its own internal data. When state changes, React automatically re-renders the component with the new data.
Without state, UIs are static. State enables interactivity — counters, form inputs, toggle menus, fetched data, loading states.
Any time data inside a component changes over time and the UI should reflect those changes: user input, toggles, counters, async data, error states.
Call useState(initialValue) inside your component. It returns [currentValue, setterFunction]. ALWAYS use the setter to update — never mutate state directly.
📚Key Terms
Re-render— React calls your component function again with new state values and updates the DOM
Setter function— The second item from useState — call it to update state and trigger re-render
Functional update— Pass a function to setter: setState(prev => prev + 1). Use when new state depends on old state
State batching— React groups multiple setState calls into one re-render for performance
Code Examples
#1useState fundamentals
"use client"; // Must be client component for hooksimport { useState } from "react";// 1. Simple counterfunction Counter() {// [current value, function to update it]const [count, setCount] = useState(0); // 0 = initial valuereturn (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>+</button><button onClick={() => setCount(count - 1)}>-</button><button onClick={() => setCount(0)}>Reset</button></div>);}// 2. Toggle statefunction Toggle() {const [isOpen, setIsOpen] = useState(false);return (<div><button onClick={() => setIsOpen(prev => !prev)}>{isOpen ? "Close" : "Open"} Menu</button>{isOpen && <nav>Menu content here</nav>}</div>);}// 3. Object state — ALWAYS spread to updatefunction UserForm() {const [user, setUser] = useState({name: "",email: "",role: "user",});// ✅ Correct — spread existing state, override specific fieldconst updateField = (field: string, value: string) => {setUser(prev => ({ ...prev, [field]: value }));};// ❌ Wrong — mutating state directly// user.name = "Ali"; // This does NOT cause a re-render!return (<form><inputvalue={user.name}onChange={(e) => updateField("name", e.target.value)}/><inputvalue={user.email}onChange={(e) => updateField("email", e.target.value)}/></form>);}
Practice This Concept
Open the playground, paste any example, and experiment. The best way to learn is by breaking things.
Open in Playground