# 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