# State Management in React
---
## 🟢 Introduction
- **Variable** → Just a normal JS variable. Changes do not trigger re-render and are reset on every render.
- **State** → Managed by React (`useState` / `useReducer`). Changes trigger re-render and persist between renders.
**Key Difference:**
- Variables store data temporarily, but React doesn’t track them.
- State is reactive — when it changes, React updates the UI automatically.
---
- **Local state** → belongs to one component.
- **Global state** → shared across components.
Why do we need state management?
---
## 💡 Why Do We Need State Management?
### 🔐 Auth (User Login)
**Case:**
- App with pages: Login, Profile, Dashboard.
- After login, every component must know the user is authenticated.
**Without State Management:**
- Pass `isLoggedIn` and `user` props down through many components (prop drilling).
- Hard to track and maintain changes.
**With State Management:**
- Auth state stored in **Context/Redux store**.
- Any component can access it easily.
- Example: Navbar switches automatically from "Login" → "Logout".
---
### 🎨 Theme (Dark/Light Mode)
**Case:**
- User toggles theme (dark ↔ light).
- Change must apply across the entire app.
**Without State Management:**
- Pass `theme` prop everywhere.
- Multiple changes needed across many files.
**With State Management:**
- Theme stored in **Context**.
- Any component can read theme with `useContext`.
- One toggle updates the entire UI.
---
### 🛒 Cart (Shopping Items)
**Case:**
- E-commerce app: User adds products from Product page.
- Cart in Navbar must show item count, Cart page must list items.
**Without State Management:**
- Pass `cartItems` props through Product Page → Navbar → Cart Page.
- Complex add/remove logic.
**With State Management:**
- Cart stored in **Redux/Zustand**.
- Product Page → `dispatch(addItem)`.
- Navbar + Cart Page read from the same source.
- Everything stays in sync without prop drilling.
---
## 🟠 useState (Local State)
**Simple hook for managing state**
```jsx
const [count, setCount] = useState(0);
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
```
✅ Easy and quick
❌ Hard to share between components
**Flow:**
```
User Action → setState() → Component Rerender → UI Updates
```
---
## 🟡 useRef (Persistent Values)
**1 - Keeps values across renders without causing rerender**
```jsx
const inputRef = useRef();
function focusInput() {
inputRef.current.focus();
}
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
```
- Store DOM references
- Keep values (previous value, timer IDs)
**Flow:**
```
Value stored in .current → Doesn't trigger rerender → Can be accessed anytime
```
**2 - Storing a value without causing rerender**
**3. Avoiding re-creating expensive objects**
👉 Example: Keep a WebSocket connection stable.
```jsx
import { useEffect, useRef } from "react";
function Chat() {
const socketRef = useRef(null);
useEffect(() => {
socketRef.current = new WebSocket("wss://example.com");
return () => socketRef.current.close();
}, []);
return <p>Chat connected...</p>;
}
```
📌 Scenario: In a chat app, you want one persistent WebSocket connection that doesn’t get recreated on every render.
---
## 🔵 useReducer (Complex State)
**Best for complex state logic**
```jsx
function reducer(state, action) {
switch (action.type) {
case 'add':
return [...state, action.payload];
default:
return state;
}
}
const [todos, dispatch] = useReducer(reducer, []);
dispatch({ type: 'add', payload: 'Learn React' });
```
✅ Great for large apps (forms, todos)
**Flow:**
```
dispatch(action) → reducer(state, action) → newState → UI Updates
```
Example
```jsx
import React, { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
);
}
export default Counter;
```
---
## 🟣 useContext (Global State Light)
**Solves prop drilling**
```jsx
const ThemeContext = createContext();
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
}
function Child() {
const theme = useContext(ThemeContext);
return <p>Theme: {theme}</p>;
}
```
✅ Easy to use
❌ Causes rerenders in big apps
### Practical Example: Auth Context
```jsx
// AuthProvider.js
import React, { createContext, useState } from "react";
export const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (username) => setUser({ name: username });
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
```
```jsx
// Navbar.js
import React, { useContext } from "react";
import { AuthContext } from "./AuthProvider";
function Navbar() {
const { user, login, logout } = useContext(AuthContext);
return (
<nav>
{user ? (
<>
<span>Welcome, {user.name} 👋</span>
<button onClick={logout}>Logout</button>
</>
) : (
<button onClick={() => login("Abdullah")}>Login</button>
)}
</nav>
);
}
```
```jsx
// App.js
import React from "react";
import { AuthProvider } from "./AuthProvider";
import Navbar from "./Navbar";
export default function App() {
return (
<AuthProvider>
<Navbar />
<h1>My Application</h1>
</AuthProvider>
);
}
```
✅ Easy to use for auth, theme, language
❌ Causes unnecessary re-renders in large apps
**Flow:**
```
Provider (value) → Context → useContext() → Consumer gets value
```
---
## 🔴 Redux (Predictable Global State)
**Core Concepts**
- **Store** → holds the state
- **Reducer** → defines how state changes
- **Action** → describes what happened
- **Dispatch** → sends the action
- **Selector** → gets the data
```jsx
// Slice (Redux Toolkit)
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state) => state + 1,
},
});
dispatch(increment());
```
✅ Predictable + DevTools
✅ Huge ecosystem (middlewares, persistence)
**Flow:**
```
Dispatch(Action) → Reducer → Store Updates → Components Re-render
```
---
## ⚪️ Other Libraries
- **Zustand** → Minimal, simple API
- **Jotai** → Atomic state
- **React Query (TanStack)** → For server state (API data, caching, syncing)
- **MobX** → Classic reactive state library
---
## 📝 Summary
- **useState** → simple local state
- **useRef** → store values without rerender
- **useReducer** → complex state logic
- **useContext** → light global state
- **Redux** → advanced global state management
- **Zustand/Jotai** → modern and simple libraries
- **React Query** → server state management