Back to articles

Beyond useState: Mastering React State Management in 2024

AuthorMajd Muhtaseb06/10/20257 minutes
Beyond useState: Mastering React State Management in 2024

Introduction

React's useState hook is a great starting point for managing component state. However, as your application grows, it can become unwieldy. This article explores advanced state management techniques to keep your React code clean, maintainable, and performant.

Context API

The Context API allows you to share state across your component tree without prop drilling.

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

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

Usage:

import { useTheme } from './ThemeContext';

const MyComponent = () => {
  const { theme, toggleTheme } = useTheme();

  return (
    <div className={`my-component ${theme}`}>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

Reducers with useReducer

The useReducer hook is useful for managing more complex state logic. It's particularly beneficial when state updates depend on previous state.

import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </>
  );
}

export default Counter;

Third-Party Libraries

Consider using libraries like Zustand or Jotai for simpler global state management compared to Redux. These libraries emphasize simplicity and ease of use.

Zustand Example:

import create from 'zustand'

const useStore = create(set => ({
  bears: 0,
  increaseBears: () => set(state => ({ bears: state.bears + 1 })),
}))

function MyComponent() {
  const bears = useStore(state => state.bears)
  const increaseBears = useStore(state => state.increaseBears)
  return (
    <div>
      {bears} bears
      <button onClick={increaseBears}>Increase bears</button>
    </div>
  )
}

Conclusion

Choosing the right state management solution depends on the complexity of your application. useState is fine for small components, but Context API, useReducer, and third-party libraries offer more powerful solutions as your application scales. Remember to choose the tool that best fits your needs and team's familiarity.