|
| 1 | +# π§© useState Examples Guide |
| 2 | + |
| 3 | +This folder contains a curated set of **8 progressively complex examples** that demonstrate how to use React's `useState` hook effectively. |
| 4 | + |
| 5 | +The goal is to **cover all major scenarios you'll encounter in real-world projects** β from the simplest counter to advanced undo/redo and persistence. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## π Why these examples? |
| 10 | + |
| 11 | +- β‘ **Incremental learning**: Start with a simple counter and gradually move to complex state management. |
| 12 | +- π§ **Key mental models**: Functional updates, batching, immutability. |
| 13 | +- π οΈ **Real-world patterns**: Forms, arrays, objects, persistence, history. |
| 14 | +- π¨ **Pitfalls included**: Stale state bugs, unnecessary recomputation, derived state anti-patterns. |
| 15 | + |
| 16 | +By the end, you'll have hands-on practice with **all scenarios where `useState` shines**. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## π Examples Overview |
| 21 | + |
| 22 | +### 1. π’ [`CounterBasic.tsx`](./examples/01_CounterBasics.tsx) |
| 23 | +**What it shows:** The simplest possible useState example |
| 24 | +**Why it matters:** This is your first step into React state management |
| 25 | +**Key concepts:** |
| 26 | +- How to initialize state with a starting value (0) |
| 27 | +- How to read the current state value |
| 28 | +- Two ways to update state: direct (`count + 1`) vs functional (`c => c + 1`) |
| 29 | +- Why functional updates are often safer |
| 30 | + |
| 31 | +**Real-world use:** Any time you need a number that goes up/down (likes, cart items, page numbers) |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +### 2. β οΈ [`FunctionalUpdateVsStale.tsx`](./examples/02_FunctionalUpdateVsStale.tsx) |
| 36 | +**What it shows:** A common bug where updates don't work as expected |
| 37 | +**Why it matters:** This bug happens in real apps and is confusing for beginners |
| 38 | +**The problem:** When you click "+2 (wrong)", it only adds 1 instead of 2 |
| 39 | +**Why it happens:** Both `setN(n + 1)` calls read the same old value of `n` |
| 40 | +**The solution:** Use `setN(v => v + 1)` - React gives you the latest value each time |
| 41 | + |
| 42 | +**Real-world use:** Any time you need multiple updates in one function (like increment counters multiple times) |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +### 3. π [`LazyInit.tsx`](./examples/03_LazyInit.tsx) |
| 47 | +**What it shows:** How to avoid expensive calculations on every render |
| 48 | +**Why it matters:** Improves performance when initial state is costly to compute |
| 49 | +**The trick:** Pass a function `useState(() => expensiveInit())` instead of calling it directly |
| 50 | +**What happens:** The expensive function only runs once, not on every render |
| 51 | + |
| 52 | +**Real-world use:** Reading from localStorage, complex calculations, API calls for initial data |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +### 4. π [`ObjectAndArrayUpdates.tsx`](./examples/04_ObjectAndArrayUpdates.tsx) |
| 57 | +**What it shows:** How to update complex data (objects and arrays) correctly |
| 58 | +**Why it matters:** React only re-renders when it detects state changes |
| 59 | +**Key rule:** Never mutate (directly change) state - always create new objects/arrays |
| 60 | +**Techniques shown:** |
| 61 | +- Object updates: `{...user, city: "Bangalore"}` (spread operator) |
| 62 | +- Array additions: `[...todos, newTodo]` |
| 63 | +- Array updates: `todos.map(t => t.id === id ? {...t, done: !t.done} : t)` |
| 64 | +- Array removals: `todos.filter(t => t.id !== id)` |
| 65 | + |
| 66 | +**Real-world use:** Todo lists, shopping carts, user profiles, any complex data |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +### 5. π [`ControlledFormMinimal.tsx`](./examples/05_ControlledFormMinimal.tsx) |
| 71 | +**What it shows:** How to manage form inputs with React state |
| 72 | +**Why it matters:** Forms are everywhere in web apps |
| 73 | +**Key concept:** "Controlled components" - React controls the input values |
| 74 | +**Pattern shown:** Store all form fields in one state object |
| 75 | +**Benefits:** Easy to reset, validate, and submit form data |
| 76 | + |
| 77 | +**Real-world use:** Login forms, registration, settings pages, any user input |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +### 6. β±οΈ [`BatchingAndTiming.tsx`](./examples/06_BatchingAndTiming.tsx) |
| 82 | +**What it shows:** How React groups multiple state updates together |
| 83 | +**Why it matters:** Understanding when your component re-renders |
| 84 | +**Key insight:** Multiple `setState` calls in the same event only cause one re-render |
| 85 | +**What you'll see:** All 3 increments happen at once, not one by one |
| 86 | + |
| 87 | +**Real-world use:** Understanding performance and when side effects run |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +### 7. β©οΈ [`UndoRedoMinimal.tsx`](./examples/07_UndoRedoMinimal.tsx) |
| 92 | +**What it shows:** How to build undo/redo functionality with just useState |
| 93 | +**Why it matters:** Shows advanced state management without external libraries |
| 94 | +**The pattern:** Three state pieces - past (history), present (current), future (undone actions) |
| 95 | +**How it works:** |
| 96 | +- New action β save current to past, clear future |
| 97 | +- Undo β move current to future, restore from past |
| 98 | +- Redo β move current to past, restore from future |
| 99 | + |
| 100 | +**Real-world use:** Text editors, drawing apps, any app where users make mistakes |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +### 8. πΎ [`PersistLocalStorage.tsx`](./examples/08_PersistLocalStorage.tsx) |
| 105 | +**What it shows:** How to save state so it survives page reloads |
| 106 | +**Why it matters:** Users expect their settings/data to persist |
| 107 | +**Two key parts:** |
| 108 | +- Lazy init: `useState(() => localStorage.getItem("theme") || "light")` |
| 109 | +- Save on change: `useEffect(() => localStorage.setItem("theme", theme), [theme])` |
| 110 | + |
| 111 | +**Real-world use:** User preferences, shopping cart, draft content, any data that should persist |
| 112 | + |
| 113 | +--- |
| 114 | + |
| 115 | +## β οΈ Common Pitfalls with `useState` |
| 116 | + |
| 117 | +- **Stale closures** β always prefer functional updates inside async code or multiple updates. |
| 118 | +- **Mutating state directly** β React won't detect changes if reference doesn't change. |
| 119 | +- **Derived state duplication** β don't store something in state if it can be computed from props/other state. |
| 120 | +- **Heavy initialization** β use lazy init to avoid recomputing on every render. |
| 121 | + |
| 122 | +--- |
| 123 | + |
| 124 | +## π€ When NOT to use `useState` |
| 125 | + |
| 126 | +- When state transitions become complex (e.g., undo/redo with multiple branches, async workflows) β prefer `useReducer`. |
| 127 | +- When global/shared state is needed β use `useContext` or state libraries. |
| 128 | +- When you need to derive data from other sources (props, memoization) β use `useMemo` instead of duplicating state. |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## π Next Steps |
| 133 | + |
| 134 | +1. Open [`UseState.tsx`](./UseState.tsx). |
| 135 | +2. Uncomment examples one by one to practice. |
| 136 | +3. Observe re-renders, console logs, and experiment. |
| 137 | +4. Compare your intuition with actual React behavior. |
| 138 | + |
| 139 | +> π‘ Mastering these patterns will make hooks like `useReducer`, `useEffect`, and `useRef` much easier to learn β since they often build on these foundations. |
0 commit comments