State Management in React.js: A Complete Beginner to Advanced Guide
#State Management #React Hooks #Redux Toolkit #Zustand #Context API

State Management in React.js

State management in React.js is about how you store, update, and share data (state) across components. As your app grows, choosing the right approach becomes very important.

I will explain this from basics to advanced, with when to use what, keeping real-world projects in mind.

What is State in React?

State is data that changes over time and affects what is rendered on the UI.

Examples:

  • Form input values
  • Logged-in user data
  • Cart items
  • API response data
  • UI state (modal open/close)

Local State (useState)

Used when state belongs to a single component

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </>
  );
}

Best for

  • Simple UI state
  • Small components

Not good for

  • Sharing data across many components

Derived State (Props)

Parent => Child data flow

function Parent() {
  const [user, setUser] = useState("Jitendra");
  return <Child user={user} />;
}

function Child({ user }) {
  return <h1>Hello {user}</h1>;
}

React's recommended first approach

Lift state up

Global State with Context API

Used when many components need the same state

Example: Auth Context

import { createContext, useContext, useState } from "react";

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  return (
    <AuthContext.Provider value={{ user, setUser }}>>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

Usage:

const { user } = useAuth();

Best for

  • Auth
  • Theme
  • Language
  • User settings

Avoid for

Very frequent updates (performance issues)

useReducer (Better for Complex State)

When state logic is complex (like Redux style)

function reducer(state, action) {
  switch (action.type) {
    case "ADD":
      return { count: state.count + 1 };
    case "SUB":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

Best for

  • Complex forms
  • State transitions
  • Predictable updates

Redux / Redux Toolkit (Enterprise Standard)

For large applications

Why Redux Toolkit?

  • Less boilerplate
  • Built-in best practices
  • DevTools support

Example:

import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "cart",
  initialState: [],
  reducers: {
    addItem: (state, action) => {
      state.push(action.payload);
    },
  },
});

export const { addItem } = cartSlice.actions;
export default cartSlice.reducer;

Best for

  • E-commerce
  • Dashboard apps
  • Multi-user state
  • Complex workflows

Zustand (Modern & Lightweight)

Very popular alternative to Redux

import { create } from "zustand";

const useStore = create((set) => ({
  cart: [],
  addToCart: (item) =>
    set((state) => ({ cart: [...state.cart, item] })),
}));

Best for

  • Medium to large apps
  • Simple API
  • High performance

Server State (React Query / TanStack Query)

For API data, NOT UI state

useQuery({
  queryKey: ["products"],
  queryFn: fetchProducts,
});

Handles:

  • Caching
  • Background refetch
  • Pagination
  • Loading & error states

Best for

  • REST / GraphQL APIs
  • Real-time data
  • Large data fetching

State Management Comparison

Use CaseBest Choice
Simple UI stateuseState
Parent-child sharingProps
App-wide configContext API
Complex logicuseReducer
Large enterprise appRedux Toolkit
Lightweight global stateZustand
API dataReact Query

Best Practices (Important)

  • Keep state as local as possible
  • Separate UI state and Server state
  • Don't overuse Context
  • Prefer Redux Toolkit or Zustand over old Redux
  • Use React Query for APIs
×
MLM PLAN
×
×