Understanding State Management in React: useState vs useReducer

Sunny Yadav
3 min readMay 12, 2024

React Hooks are reusable functions that provide access to state and other React capabilities without writing a class component. Introduced in React version 16.8, Hooks allow you to use features like state management, context, and lifecycle methods in functional components.

When building applications with React, managing state is a critical aspect that can determine the complexity and performance of your app. Two hooks that stand out for state management are useState and useReducer. While both serve the same purpose—managing state—they offer different approaches that applies to various scenarios.

What is useState?

useState is a hook that lets you add React state to function components. It accepts a value as an argument and returns an array having two elements, first element is the current value and second element is the callback function to update the state’s value. It’s a straightforward way to track values in your app that need to render and re-render with changes. It’s perfect for simple state variables like numbers, strings, or booleans.

Here’s a basic example of useState:

import { useState } from 'react';

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

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

In above code, useState is used to keep track of a count state variable. The setCount function is used to update this state.

What is useReducer?

useReducer is another hook that is more suited for managing state objects with multiple sub-values or when the next state depends on the previous one. It accepts two arguments, firts one is reducer function and another one is initial state value, and returns an array with two elements. First element is the current state’s value and second element is the dispatch action to update the state’s value. It’s ideal for more complex state logic that involves multiple sub-values or when managing a group of related states.

Here’s an example of useReducer:

import { 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 (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</div>
);
}

In above example, useReducer provides a dispatch method to send actions to a reducer function that determines how state is updated.

When to Use Which?

Use useState when:

  • You have simple state logic that doesn’t involve multiple sub-values.
  • The state doesn’t depend on the previous state.
  • Your state transitions are simple and not tied to complex logic.

Use useReducer when:

  • You have complex state logic that involves multiple sub-values or when managing related state transitions.
  • The next state depends on the previous one.
  • You want to optimize performance for components that trigger deep updates because useReducer will avoid unnecessary renders.

Conclusion

Both useState and useReducer are powerful hooks for state management in React. Choosing between them depends on the complexity of your state logic. For simple states, useState is your best bet. For more complex scenarios, useReducer offers more control and scalability.

Remember, the right choice will make your code more readable, maintainable, and efficient.

--

--

Sunny Yadav

Frontend engineer. Loves clean code & user-friendly design. Proficient in HTML, CSS, JS, TS, React.JS and Next.JS. Constantly learning