Back to articles

Beyond useState: Advanced React State Management Patterns

AuthorMajd Muhtaseb05/05/20257 minutes
Beyond useState: Advanced React State Management Patterns

Introduction

While useState is perfect for simple React components, larger applications demand more robust state management solutions. This article delves into three popular alternatives: Context API with useReducer, and Zustand.

Context API with useReducer

Context API allows you to share state across your component tree without prop drilling. Combining it with useReducer provides a structured way to manage complex state updates.

import React, { createContext, useContext, useReducer } from 'react';

const CounterContext = createContext();

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const CounterProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
};

const useCounter = () => {
  const context = useContext(CounterContext);
  if (!context) {
    throw new Error('useCounter must be used within a CounterProvider');
  }
  return context;
};

const Counter = () => {
  const { state, dispatch } = useCounter();

  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

export { CounterProvider, Counter };

To use it:

import { CounterProvider, Counter } from './CounterContext';

function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  );
}

export default App;

Zustand

Zustand is a small, fast, and scalable bearbones state management solution. It uses simplified flux principles.

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

const Counter = () => {
  const { count, increment, decrement } = useStore();

  return (
    <div>
      Count: {count}
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

Conclusion

Choosing the right state management solution depends on your application's complexity. Context API with useReducer offers structured control, while Zustand provides a simpler, more streamlined approach. Consider these options when useState no longer suffices for your React projects.