DEV Community

Cover image for Simplifying React Hooks: useReducer ๐Ÿ’ฏ
Ali Samir
Ali Samir

Posted on

3 1 1 1

Simplifying React Hooks: useReducer ๐Ÿ’ฏ

Introduction

When managing complex states in React, useState often falls short. This is where useReducer comes in, providing a more structured way to handle state transitions.

In this article, weโ€™ll explain useReducer, provide TypeScript-based examples to ensure type safety and clarity and discuss its benefits.


What is useReducer?

useReducer is a React Hook that provides an alternative to useState. It is useful for predictably handling state logic, especially in complex scenarios where multiple state updates depend on each other.

Syntax:

const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode
  • state: The current state value.

  • dispatch: A function used to trigger state changes.

  • reducer: A function that takes the current state and an action, then returns a new state.

  • initialState: The default state when the component mounts.


Why Use useReducer Instead of useState?

  • Better state management: Suitable for complex state logic with multiple transitions.

  • Predictability: State transitions are centralized in a reducer function.

  • Improved debugging: Actions make state changes more traceable.


Implementing useReducer in TypeScript

Letโ€™s implement a simple counter with useReducer.

Step 1: Define State and Actions

type State = {
  count: number;
};

type Action = { type: "INCREMENT" } | { type: "DECREMENT" } | { type: "RESET" };
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Reducer Function

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    case "RESET":
      return { count: 0 };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

Step 3: Use useReducer in a Component

import { useReducer } from "react";

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

  return (
    <div>
      <h2>Count: {state.count}</h2>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      <button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

Handling Complex State

When dealing with more complex states, useReducer scales better than useState. For example, managing a formโ€™s state:

Define State and Actions

type FormState = {
  username: string;
  email: string;
};

type FormAction =
  | { type: "CHANGE_USERNAME"; payload: string }
  | { type: "CHANGE_EMAIL"; payload: string }
  | { type: "RESET" };
Enter fullscreen mode Exit fullscreen mode

Create the Reducer Function

const formReducer = (state: FormState, action: FormAction): FormState => {
  switch (action.type) {
    case "CHANGE_USERNAME":
      return { ...state, username: action.payload };
    case "CHANGE_EMAIL":
      return { ...state, email: action.payload };
    case "RESET":
      return { username: "", email: "" };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

Use in a Component

const Form = () => {
  const [state, dispatch] = useReducer(formReducer, { username: "", email: "" });

  return (
    <div>
      <input
        type="text"
        value={state.username}
        onChange={(e) => dispatch({ type: "CHANGE_USERNAME", payload: e.target.value })}
        placeholder="Username"
      />
      <input
        type="email"
        value={state.email}
        onChange={(e) => dispatch({ type: "CHANGE_EMAIL", payload: e.target.value })}
        placeholder="Email"
      />
      <button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
      <p>Username: {state.username}</p>
      <p>Email: {state.email}</p>
    </div>
  );
};

export default Form;
Enter fullscreen mode Exit fullscreen mode

When Should You Use useReducer?

  • When state logic is complex and interdependent.

  • When the next state depends on the previous one.

  • When you need more predictable state transitions.

  • When managing state in large-scale applications.


Conclusion

useReducer is a powerful alternative to useState, especially for managing complex state logic.

Using it with TypeScript enhances type safety and maintainability. By structuring state updates through actions and a reducer function, useReducer makes React applications more scalable and predictable.


๐ŸŒ Connect With Me On:

๐Ÿ“ LinkedIn
๐Ÿ“ X (Twitter)
๐Ÿ“ Telegram
๐Ÿ“ Instagram

Happy Coding!

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

๐Ÿ‘‹ Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someoneโ€™s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay