State in React
State is data that changes over time within a component. Unlike props, state is managed internally by the component and can be updated.
useState Hook
The useState hook is how you add state to function components:
jsx
1import { useState } from 'react'
2
3function Counter() {
4 // Declare state variable 'count' with initial value 0
5 const [count, setCount] = useState(0)
6
7 return (
8 <div>
9 <p>Count: {count}</p>
10 <button onClick={() => setCount(count + 1)}>
11 Increment
12 </button>
13 </div>
14 )
15}useState Syntax
jsx
1const [stateValue, setterFunction] = useState(initialValue)
2
3// Examples
4const [count, setCount] = useState(0)
5const [name, setName] = useState('')
6const [isOpen, setIsOpen] = useState(false)
7const [user, setUser] = useState(null)
8const [items, setItems] = useState([])
9const [form, setForm] = useState({ email: '', password: '' })Updating State
Simple Updates
jsx
1function Counter() {
2 const [count, setCount] = useState(0)
3
4 const increment = () => setCount(count + 1)
5 const decrement = () => setCount(count - 1)
6 const reset = () => setCount(0)
7
8 return (
9 <div>
10 <p>{count}</p>
11 <button onClick={increment}>+</button>
12 <button onClick={decrement}>-</button>
13 <button onClick={reset}>Reset</button>
14 </div>
15 )
16}Functional Updates
When new state depends on previous state, use a function:
jsx
1function Counter() {
2 const [count, setCount] = useState(0)
3
4 // ❌ Might not work correctly with rapid clicks
5 const increment = () => setCount(count + 1)
6
7 // ✅ Always correct - uses previous state
8 const increment = () => setCount(prev => prev + 1)
9
10 // Multiple increments
11 const incrementBy3 = () => {
12 setCount(prev => prev + 1)
13 setCount(prev => prev + 1)
14 setCount(prev => prev + 1)
15 }
16
17 return <button onClick={increment}>{count}</button>
18}State with Different Types
String State
jsx
1function NameInput() {
2 const [name, setName] = useState('')
3
4 return (
5 <div>
6 <input
7 value={name}
8 onChange={(e) => setName(e.target.value)}
9 placeholder="Enter name"
10 />
11 <p>Hello, {name || 'stranger'}!</p>
12 </div>
13 )
14}Boolean State
jsx
1function Toggle() {
2 const [isOn, setIsOn] = useState(false)
3
4 return (
5 <button onClick={() => setIsOn(prev => !prev)}>
6 {isOn ? 'ON' : 'OFF'}
7 </button>
8 )
9}
10
11function Modal() {
12 const [isOpen, setIsOpen] = useState(false)
13
14 return (
15 <>
16 <button onClick={() => setIsOpen(true)}>Open Modal</button>
17
18 {isOpen && (
19 <div className="modal">
20 <h2>Modal Content</h2>
21 <button onClick={() => setIsOpen(false)}>Close</button>
22 </div>
23 )}
24 </>
25 )
26}Array State
jsx
1function TodoList() {
2 const [todos, setTodos] = useState([])
3 const [input, setInput] = useState('')
4
5 const addTodo = () => {
6 if (input.trim()) {
7 setTodos([...todos, { id: Date.now(), text: input, done: false }])
8 setInput('')
9 }
10 }
11
12 const toggleTodo = (id) => {
13 setTodos(todos.map(todo =>
14 todo.id === id ? { ...todo, done: !todo.done } : todo
15 ))
16 }
17
18 const deleteTodo = (id) => {
19 setTodos(todos.filter(todo => todo.id !== id))
20 }
21
22 return (
23 <div>
24 <input value={input} onChange={(e) => setInput(e.target.value)} />
25 <button onClick={addTodo}>Add</button>
26
27 <ul>
28 {todos.map(todo => (
29 <li key={todo.id}>
30 <span
31 style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
32 onClick={() => toggleTodo(todo.id)}
33 >
34 {todo.text}
35 </span>
36 <button onClick={() => deleteTodo(todo.id)}>Delete</button>
37 </li>
38 ))}
39 </ul>
40 </div>
41 )
42}Object State
jsx
1function Form() {
2 const [form, setForm] = useState({
3 firstName: '',
4 lastName: '',
5 email: ''
6 })
7
8 const handleChange = (e) => {
9 const { name, value } = e.target
10 setForm(prev => ({
11 ...prev,
12 [name]: value
13 }))
14 }
15
16 return (
17 <form>
18 <input
19 name="firstName"
20 value={form.firstName}
21 onChange={handleChange}
22 placeholder="First Name"
23 />
24 <input
25 name="lastName"
26 value={form.lastName}
27 onChange={handleChange}
28 placeholder="Last Name"
29 />
30 <input
31 name="email"
32 value={form.email}
33 onChange={handleChange}
34 placeholder="Email"
35 />
36 <p>Hello, {form.firstName} {form.lastName}</p>
37 </form>
38 )
39}Multiple State Variables
jsx
1function UserProfile() {
2 const [name, setName] = useState('')
3 const [age, setAge] = useState(0)
4 const [isEditing, setIsEditing] = useState(false)
5 const [errors, setErrors] = useState([])
6
7 // Each state updates independently
8}State vs Props
| State | Props |
|---|---|
| Managed inside component | Passed from parent |
| Can be changed | Read-only |
| Triggers re-render when changed | Triggers re-render when parent updates |
| Component's own data | Component's configuration |
Lazy Initial State
For expensive initial computations:
jsx
1// ❌ Runs on every render
2const [items, setItems] = useState(expensiveComputation())
3
4// ✅ Only runs once
5const [items, setItems] = useState(() => expensiveComputation())State Batching
React batches multiple state updates for performance:
jsx
1function Counter() {
2 const [count, setCount] = useState(0)
3
4 const handleClick = () => {
5 setCount(count + 1)
6 setCount(count + 1)
7 setCount(count + 1)
8 // count only increases by 1, not 3!
9 // All updates see the same 'count' value
10 }
11
12 const handleClickCorrect = () => {
13 setCount(prev => prev + 1)
14 setCount(prev => prev + 1)
15 setCount(prev => prev + 1)
16 // count increases by 3 ✅
17 }
18}Don't Mutate State
Always create new objects/arrays:
jsx
1// ❌ Wrong - mutating state directly
2const handleClick = () => {
3 user.name = 'New Name' // Mutation!
4 setUser(user)
5}
6
7// ✅ Correct - creating new object
8const handleClick = () => {
9 setUser({ ...user, name: 'New Name' })
10}
11
12// ❌ Wrong - mutating array
13const addItem = () => {
14 items.push(newItem) // Mutation!
15 setItems(items)
16}
17
18// ✅ Correct - creating new array
19const addItem = () => {
20 setItems([...items, newItem])
21}State is what makes React components interactive and dynamic!
