Chapter 5: State & useState Hook
1. What is State in React?
State is memory that a component can remember between renders. It’s the data that can change over time and when it changes → React automatically re-renders the component (and updates what you see on the screen).
Examples of state:
- A counter that increases when you click a button
- Whether a modal is open or closed
- The text typed in a search box
- The items in a shopping cart
- Whether dark mode is on or off
Key points about state:
- State is private to each component instance
- Only the component itself can change its own state
- When state changes → React re-renders the component (and its children)
- State makes your UI dynamic and responsive
2. useState Hook – Basics
useState is the simplest and most common way to add state to a functional component.
|
0 1 2 3 4 5 6 |
import { useState } from 'react'; |
Syntax:
|
0 1 2 3 4 5 6 |
const [stateVariable, setStateVariable] = useState(initialValue); |
- stateVariable → the current value of the state
- setStateVariable → the function to update the state
- initialValue → the starting value (can be number, string, boolean, object, array…)
Important: Calling setStateVariable tells React to update the state and re-render the component with the new value.
3. Your First Counter Example (do this right now!)
Open src/App.tsx and replace everything with this:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import { useState } from 'react'; function App() { // Declare state: count starts at 0 const [count, setCount] = useState(0); return ( <div style={{ padding: '40px', textAlign: 'center', fontFamily: 'system-ui', maxWidth: '600px', margin: '0 auto' }}> <h1 style={{ color: '#646cff' }}>React Counter App</h1> <h2 style={{ fontSize: '4rem', margin: '30px 0' }}> {count} </h2> <div style={{ display: 'flex', gap: '20px', justifyContent: 'center' }}> <button onClick={() => setCount(count + 1)} style={{ padding: '12px 30px', fontSize: '20px', backgroundColor: '#646cff', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }} > +1 </button> <button onClick={() => setCount(count - 1)} style={{ padding: '12px 30px', fontSize: '20px', backgroundColor: '#ff6b6b', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }} > -1 </button> <button onClick={() => setCount(0)} style={{ padding: '12px 30px', fontSize: '20px', backgroundColor: '#4ecdc4', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }} > Reset </button> </div> <p style={{ marginTop: '40px', fontSize: '18px' }}> Current count: <strong>{count}</strong> </p> </div> ); } export default App; |
What’s happening here?
- const [count, setCount] = useState(0); → We create state called count, starting at 0
- When you click +1 → setCount(count + 1) runs
- React updates count to the new value
- React re-renders the component
- The screen shows the new count value
4. Updating State Correctly (Very Important!)
Wrong way (common beginner mistake):
|
0 1 2 3 4 5 6 7 8 9 |
<button onClick={() => { count = count + 1; // ← This does NOT work! // count is just a variable, changing it does nothing }}> |
Correct ways:
Way 1 (most common):
|
0 1 2 3 4 5 6 |
setCount(count + 1); |
Way 2 (safer when you depend on previous state – always recommended):
|
0 1 2 3 4 5 6 |
setCount(prevCount => prevCount + 1); |
Why use the function form? If you do multiple updates in a row, React batches them. The function form guarantees you always get the latest value.
Example – multiple updates in one click:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<button onClick={() => { // Wrong: may not update correctly because of batching // setCount(count + 1); // setCount(count + 1); // ← might only +1 instead of +2 // Correct: always uses latest value setCount(prev => prev + 1); setCount(prev => prev + 1); // → +2 guaranteed }} > +2 (correct way) </button> |
5. Multiple State Variables (Realistic Example)
Let’s build a simple Todo form with multiple pieces of state.
Create src/components/TodoForm.tsx
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import { useState } from 'react'; function TodoForm() { // Multiple independent state variables const [task, setTask] = useState(''); const [priority, setPriority] = useState('medium'); const [dueDate, setDueDate] = useState(''); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!task.trim()) return; alert(`New task added!\nTask: ${task}\nPriority: ${priority}\nDue: ${dueDate || 'No date'}`); // Reset form setTask(''); setPriority('medium'); setDueDate(''); }; return ( <div style={{ margin: '40px auto', maxWidth: '500px' }}> <h2 style={{ color: '#646cff' }}>Add New Task</h2> <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}> <input type="text" value={task} onChange={(e) => setTask(e.target.value)} placeholder="What needs to be done?" style={{ padding: '12px', fontSize: '16px', borderRadius: '8px', border: '1px solid #ddd' }} /> <select value={priority} onChange={(e) => setPriority(e.target.value)} style={{ padding: '12px', fontSize: '16px', borderRadius: '8px' }} > <option value="low">Low</option> <option value="medium">Medium</option> <option value="high">High</option> </select> <input type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} style={{ padding: '12px', fontSize: '16px', borderRadius: '8px' }} /> <button type="submit" style={{ padding: '12px', fontSize: '18px', backgroundColor: '#646cff', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }} > Add Task </button> </form> </div> ); } export default TodoForm; |
Use it in App.tsx:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import TodoForm from './components/TodoForm'; function App() { return ( <div style={{ padding: '20px' }}> <h1 style={{ textAlign: 'center', color: '#646cff' }}>My Todo App</h1> <TodoForm /> </div> ); } |
Summary – Chapter 5 Key Takeaways
- State = memory that survives re-renders
- useState = hook to add state to functional components
- Always use function updater form (prev => …) when new state depends on previous
- You can have multipleuseState calls in one component
- Calling setState → triggers re-render with new state value
- State is local to each component instance
Mini Homework:
- Create a component LikeButton with:
- State: isLiked (boolean, starts false)
- State: likeCount (number, starts 0)
- Button that toggles like/unlike and increases/decreases count
- Bonus: Add a heart icon that changes color when liked (use emoji ❤️)
