Skip to main content

Command Palette

Search for a command to run...

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

Published
5 min read
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 state

  • useState(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:

  1. Pass it up to CounterPage

  2. Pass it up to App

  3. Pass 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

  1. useState is perfect for local, component-specific state

  2. Context API solves the prop drilling problem

  3. Context creates a "global" state accessible anywhere in your app

  4. You still use useState inside Context - Context just makes it available everywhere

  5. Always wrap your app with the Provider to use Context

  6. 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! 🚀

More from this blog

S

Salome Githinji - Tech Hub

18 posts