Understanding State Management in React

Explore the essentials of state management in React, from useState and useReducer to modern state libraries, with clear examples and best practices.


Back to Home

Table of content

Introduction

State management is at the heart of every React application. Whether you’re building a simple todo list or a complex enterprise UI, understanding how state works in React is essential for writing bug-free, maintainable code. In this article, you’ll learn the fundamentals of managing state in React, compare built-in tools like useState and useReducer, and explore when to consider external libraries.

What is State in React?

State represents any data that can change over time in your app’s UI. This includes things like user input, fetched data, toggled elements, and more. State helps React components respond and re-render appropriately to user actions.

Local vs. Global State

  • Local State: Managed within a single component, often using useState.
  • Global State: Shared across many components, commonly managed using context or external libraries.

Using useState for Local State

The useState hook is React’s simplest way to create and update state in functional components.

import React, { useState } from 'react';

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

  return (
    <div>
      Count: {count}
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

export default Counter;

When to Use useReducer

useReducer is better for complex state logic or multiple related values, helping you manage state transitions more predictably.

import React, { useReducer } from 'react';

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(); // Handle unexpected actions
  }
}

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

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

Lifting State Up

When two or more components need to share state, lift the state up to their nearest common ancestor and pass it down via props:

function Parent() {
  const [shared, setShared] = useState('data');

  return (
    <>
      <Child shared={shared} setShared={setShared} />
      {/* Other components that need access to shared state */}
    </>
  );
}

function Child({ shared, setShared }) {
  return (
    <div>
      <p>Shared Data: {shared}</p>
      <button onClick={() => setShared('updated')}>Update Shared Data</button>
    </div>
  );
}

Context API for Global State

The built-in Context API lets you create global state accessible throughout your component tree—no prop drilling required.

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

const MyContext = React.createContext(); // Create a context

function Provider({ children }) {
  const [value, setValue] = useState('shared'); // Initial state

  return (
    <MyContext.Provider value={{ value, setValue }}> {/* Provide the context */}
      {children}
    </MyContext.Provider>
  );
}

function Consumer() {
  const { value, setValue } = useContext(MyContext); // Consume the context

  return (
    <div>
      <p>Value: {value}</p>
      <button onClick={() => setValue('updated')}>Update Value</button>
    </div>
  );
}

When to Use External State Libraries

For very large apps with deeply nested components, libraries like Redux, Zustand, or Recoil provide features like middleware, selectors, and time-travel debugging. Use them if:

  • Your state logic is too complex for useReducer or Context.
  • You want to persist state or interact with APIs globally.
  • Many components need access or updates to the same data.

Best Practices for State Management in React

  • Start with useState and keep state as local as possible.
  • Lift state up only when needed.
  • Use Context API for simple global state.
  • Reach for external libraries when your state becomes hard to manage.

Conclusion

Understanding and choosing the right state management approach keeps your React application scalable and clear. Master the fundamentals first, and scale up your tools only as your project demands. For more insights on React and web development, stay tuned on fulldev.pl!

Context API
React
Redux
State Management
useReducer
useState
Web Development