
React State Management - Part 1: Getting Started with Built-in Tools
Published: 7/21/2025
If you've been working with React for a bit, you've probably heard the word "state" tossed around a lot. And if you're anything like I was early on, you might be wondering:
Why is everyone so obsessed with state?

Well, state is what makes your app come alive. It’s what lets your UI respond when a user types something, clicks a button, or receives data from an API. In short: no state, no interactivity.
In this post, we’ll talk about:
- What “state” really means in React
- Why managing it properly matters (especially as your app grows)
- The tools React gives you out of the box
- When you should use those tools — and when you shouldn’t
So... What Is State?
Think of state as the memory of your app. It's any data that can change over time and influences what gets rendered.
A few types of state:
- UI State — toggling a modal, switching tabs, light/dark theme
- Form State — inputs, validation, error messages
- App State — user login status, cart contents, selected items
- Server State — fetched data like blog posts or product listings
- Global State — shared across multiple parts of the app
Managing state isn’t hard at first… but once your app grows, it can turn into spaghetti real quick
React’s Built-In Tools for Managing State
Before you reach for Redux or Zustand, it’s good to know that React already gives you some pretty solid tools.
Let’s go over the big three:
useState - The OG
// Step 1: Import the useState hook from React
import { useState } from "react";
// Step 2: Create a simple Counter component
function Counter() {
// Step 3: Declare a state variable 'count' with initial value 0
// 'setCount' is the function used to update 'count'
const [count, setCount] = useState(0);
// Step 4: Render a button that shows the current count
// When clicked, it increments the count by 1 using setCount
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
Use it when:
You need a small piece of state that only lives in one component — like toggling a menu or tracking form input.
useReducer - For When Things Get a Bit Messy
Sometimes, your state logic gets complicated. Like, “I have 4 different actions and need to update nested fields” complicated.
That’s where useReducer comes in.
import { useReducer } from "react";
// Step 1: Define your reducer function
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() {
// Step 2: Initialize state using useReducer
const [state, dispatch] = useReducer(reducer, { count: 0 });
// Step 3: Use state and dispatch in your JSX
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</div>
);
}
Use it when:
- State updates depend on previous state
- You’re managing multiple related values
- You want Redux-style logic without Redux
Context API — Share State Without Going Crazy
You’ve probably run into prop drilling — passing the same value through 3... 4... 5 components just to get it where it’s needed. 😩
Context API helps fix that.
// Step 1: Import necessary hooks and createContext from React
import React, { createContext, useContext, useState } from "react";
// Step 2: Create a new context (can be placed in a separate file for larger apps)
const ThemeContext = createContext();
// Step 3: Create a provider component that wraps your app or part of it
function App() {
// Declare a piece of state to hold the current theme
const [theme, setTheme] = useState("light");
// Provide the theme value and updater function to children
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
);
}
// Step 4: A nested component that needs access to the theme
function Page() {
// Use useContext to access the context value
const { theme, setTheme } = useContext(ThemeContext);
// Render something based on the current theme
return (
<div className={theme}>
<h2>Current Theme: {theme}</h2>
<button onClick={() => setTheme("dark")}>Switch to Dark</button>
<button onClick={() => setTheme("light")}>Switch to Light</button>
</div>
);
}
Use it when:
- You need to share state (like a theme or user) across multiple components.
Heads up:
Context re-renders all consumers whenever the value changes. So it’s not ideal for high-frequency updates like input values or mouse positions.
What Problems Are We Solving with State Management?
Here’s where it gets real. As your app grows, state management becomes less about what you want and more about managing complexity.
Some common pain points:
Prop Drilling
Too many props passed through too many layers = confusion + boilerplate
Shared State
How do two components on totally different branches of the tree talk to each other?
Async Data
Fetching stuff from APIs and juggling loading states, success, and errors can be a headache.
Performance
Too many unnecessary re-renders = slow app = unhappy users
So... When Are React’s Built-Ins Enough?
Honestly? A lot of the time.
Here’s a quick rule of thumb:
- ✅ Small app, simple logic:
→ Yup! Stick with useState and useReducer. - ✅ Local UI state (modals, toggles, inputs):
→ Definitely. useState handles this beautifully. - ✅ Occasionally shared state (like theme, locale):
→ Yes, and React’s Context API is perfect for this. - 🔴 Complex global state with lots of interactions:
→ Probably not. You might want something like Redux Toolkit or Zustand. - 🔴 High-frequency updates or async server data (e.g. chats, live data):
→ You’ll need a dedicated tool like React Query or Zustand for better performance and control.
TL;DR

- React gives you powerful tools — useState , useReducer, and Context to manage a lot of your app's state needs.
- They’re great for local or lightly shared state.
- But once things get complex (especially with async data or deep sharing), it’s time to explore external libraries.
Up Next: Part 2 — Client-Side State Libraries (Redux, Zustand & more)
We’ll dive into the most popular libraries for managing global state, compare them, and help you choose the right one for your app.