🧠 Introduction
When building React applications, managing data and keeping your UI in sync with that data can become tricky — especially as your app grows.
That’s where state management comes in.
State management in React refers to the way you store, update, and share data between components.
It helps React apps stay organized, predictable, and easy to debug.
🔍 What is “State” in React?
In simple terms —
👉 State is data that changes over time and affects how your component looks or behaves.
Example
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h2>🧮 Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>➕ Increment</button>
</div>
);
}
Here
When you click the button, React re-renders the component with the new state.
⚙️ Types of State in React
State in React can be classified into different types depending on how and where it’s used 👇
1. Local State 🏠
A state that belongs to a single component.
Example: toggling a modal, handling form inputs, or managing counters.
const [isOpen, setIsOpen] = useState(false);
2. Global State 🌍
When data needs to be shared across multiple components, you use global state.
Example: user authentication info, theme settings, or cart data in an e-commerce app.
You can handle this using: Context API, Redux, Zustand, Recoil, etc.
3. Server State 🌐
State that comes from an external server — such as fetched API data.
Handled using
4. URL State 🔗
Data stored in the URL — like query parameters or path variables.
Example: /product/123 or ?search=phone
You can manage it using:
import { useSearchParams } from "react-router-dom";
🧩 How React Handles State (The Simple Way)
React uses the useState() hook for local state and props to share data between components.
Example
function Child({ name }) {
return <h3>👋 Hello, {name}</h3>;
}
function Parent() {
const [user, setUser] = useState("Asfaque");
return <Child name={user} />;
}
This works fine for small apps. But as your app grows…
→ Passing data down through multiple layers (props drilling) becomes messy. 😩
That’s when state management tools become essential.
🧠 State Management Approaches in React
Let’s explore the most popular methods 👇
🧰 1. React Context API (Built-in)
Used to share data across components without props drilling.
Great for small to medium apps.
// Context.js
import { createContext, useState } from "react";
export const UserContext = createContext();
export function UserProvider({ children }) {
const [user, setUser] = useState("Asfaque");
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
Now consume it anywhere 👇
import { useContext } from "react";
import { UserContext } from "./Context";
function Profile() {
const { user } = useContext(UserContext);
return <h2>👤 Welcome, {user}!</h2>;
}
✅ Pros
Simple and built-in
No extra libraries
⚠️ Cons
⚡ 2. Redux
Redux is the most popular global state management library.
It uses a central store that holds the entire app’s state.
Flow
Actions → Reducers → Store → UI
// counterSlice.js (using Redux Toolkit)
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; }
}
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Then connect it using useSelector and useDispatch.
✅ Pros
⚠️ Cons
💫 3. Zustand
Zustand is a lightweight state management library — much simpler than Redux.
import { create } from "zustand";
const useStore = create((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 }))
}));
function Counter() {
const { count, increase } = useStore();
return (
<div>
<h2>🧮 Count: {count}</h2>
<button onClick={increase}>➕ Increment</button>
</div>
);
}
✅ Pros
🔄 4. React Query (for Server State)
If your app depends heavily on API data, React Query (also called TanStack Query) is amazing.
It automatically manages caching, fetching, and background updates.
import { useQuery } from "@tanstack/react-query";
function Users() {
const { data, isLoading } = useQuery({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then((res) => res.json())
});
if (isLoading) return <p>⏳ Loading...</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
✅ Pros
⚠️ Cons
🧭 When to Use Which?
Use Case Best Choice
Local Component State useState, useReducer
Share Small Data (like Theme/User) Context API
Large Scale App (Complex State) Redux Toolkit
Lightweight & Simple Store Zustand
API/Server Data React Query
🧩 Best Practices for State Management
✅ Keep state as local as possible
✅ Avoid unnecessary global state
✅ Split large states into smaller logical parts
✅ Use memoization (useMemo, useCallback) to prevent re-renders
✅ Use tools like React DevTools or Redux DevTools to debug
🚀 Conclusion
State management is the heart of every React app ❤️.
From simple local states to complex global stores, managing state effectively keeps your app predictable, scalable, and easy to maintain.
Start simple with useState and Context API, and when your app grows — explore tools like Redux, Zustand, or React Query.
Because great apps don’t just have good UI —
> They have a well-managed state behind the scenes. 🧠⚙️