Understanding React State Management: From useState to Context API (Part 1)

Introduction
When building React applications, managing data (or "state") is one of the most important concepts you'll work with. In this article, we'll explore two ways to handle state in React: starting with the simple useState hook and then learning why and when we need Context API.
Part 1: Understanding useState Hook
What is State?
Think of state as your application's memory. It's the data that can change over time based on user actions. For example, in a counter app:
The current count number is state
When you click a button, the state changes
React automatically updates what you see on the screen
How useState Works
The useState hook is React's simplest way to add state to your components. Here's how it works:
javascript
const [count, setCount] = useState(0);
Let's break this down:
count- This is your state variable (the current value)setCount- This is the function you use to update the stateuseState(0)- This sets the initial value to 0
A Simple Counter Example
javascript
const CounterApp = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count > 0 ? count - 1 : 0);
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
This works perfectly! When you click a button, the count updates and the screen refreshes with the new number.
The Problem with useState
But what if you need to show the count in multiple places? For example:
Display the count in your counter page
Also show it in the header at the top
Maybe show it in a footer or sidebar too
You might think: "Easy! Just pass the count as a prop!" But here's what happens:
App
├── Header (needs count)
├── Sidebar (needs count)
└── CounterPage
└── CounterComponent (has the count)
To get the count from CounterComponent to Header, you'd have to:
Pass it up to
CounterPagePass it up to
AppPass it down to
Header
This is called "prop drilling" - passing props through multiple components that don't even use them! It's messy, hard to maintain, and frustrating.
Part 2: Enter Context API
What is Context API?
Context API is like creating a global storage box that any component can access directly, without passing props through every level. Think of it as a radio station - any radio (component) can tune in to listen, without needing wires connecting everything.
The Three Main Parts of Context API
1. Creating the Context
First, we create our "storage box":
const CountContext = createContext();
2. The Provider (The Broadcaster)
The Provider wraps your app and makes the state available to everyone:
export const CountProvider = ({ children }) => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count > 0 ? count - 1 : 0);
return (
<CountContext.Provider value={{ count, increment, decrement }}>
{children}
</CountContext.Provider>
);
}
The Provider holds the state and the functions to change it. Any component inside it can access these.
3. The Custom Hook (Easy Access)
We create a custom hook to make accessing the context simple:
export const useCount = () => {
const context = useContext(CountContext);
if (!context) {
throw new Error("useCount must be used within a CountProvider");
}
return context;
}
This hook checks if you're using the context correctly and gives you easy access to the count data.
How to Use Context API in Your App
Step 1: Wrap Your App
In your _app.tsx file, wrap everything with the Provider:
export default function App({ Component, pageProps }) {
return (
<CountProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</CountProvider>
);
}
Step 2: Use It Anywhere
Now ANY component can access the count:
// In Header component
const Header = () => {
const { count } = useCount();
return (
<header>
<p>Current count: {count}</p>
</header>
);
}
// In Counter component
const CounterApp = () => {
const { count, increment, decrement } = useCount();
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
Notice how both components can access count directly! No prop drilling needed!
When to Use What?
Use useState When:
State is only needed in one component
You have a simple, small application
The data doesn't need to be shared
Use Context API When:
Multiple components need the same data
You want to avoid prop drilling
You need global state (like user info, theme, language settings)
Components are far apart in your component tree
Real-World Example: The Counter App
Imagine you're building a shopping cart:
The Header needs to show the total items
The Product Page needs to add/remove items
The Cart Page needs to display everything
With useState, you'd pass props everywhere. With Context API:
const CartProvider = ({ children }) => {
const [items, setItems] = useState([]);
const addItem = (item) => setItems([...items, item]);
const removeItem = (id) => setItems(items.filter(i => i.id !== id));
return (
<CartContext.Provider value={{ items, addItem, removeItem }}>
{children}
</CartContext.Provider>
);
}
Now any component can access and modify the cart!
Key Takeaways
useState is perfect for local, component-specific state
Context API solves the prop drilling problem
Context creates a "global" state accessible anywhere in your app
You still use useState inside Context - Context just makes it available everywhere
Always wrap your app with the Provider to use Context
Custom hooks make accessing Context clean and easy
Conclusion
Think of useState as your personal notebook - great for your own notes. Context API is like a shared whiteboard in an office - everyone can see it and contribute to it. Both have their place, and knowing when to use each makes you a better React developer!
Start with useState for simple needs, and graduate to Context API when you need to share state across distant components.
Happy coding! 🚀




