Skip
Arish's avatar

5. State in React


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

StateProps
Managed inside componentPassed from parent
Can be changedRead-only
Triggers re-render when changedTriggers re-render when parent updates
Component's own dataComponent'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!