DEV Community

Ayako yk
Ayako yk

Posted on

Understanding React useReducer

In the previous blog post, I talked about useContext, where props can be accessed without prop drilling. However, as an app becomes more complex, it becomes challenging to manage all the states. Consequently, useReducer is often employed in conjunction with useContext.

useReducer is one of the React Hooks. Although I've used other Hooks for projects, I've never used useReducer, to be honest. Before delving into the usage of useReducer and useContext, I'd like to talk about what useReducer is.

Syntax

const [state, dispatch] = useReducer(reducer, initialArg, init?)
Enter fullscreen mode Exit fullscreen mode

useReducer is a state management hook, similar to useState. It takes three parameters: reducer (a function that specifies how the state gets updated), initialArg, and an optional init (an initializer function that returns the initial state. If it's not provided, the initial state is set to initialArg). The useReducer hook returns an array containing state and dispatch (a function).

Here's an example code from React's official documentation.
If you're familiar with Redux, it might be easier to understand to integrate the idea by reading the code below.
react.dev

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

const initialState = { name: 'Taylor', age: 42 };

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

  function handleButtonClick() {
    dispatch({ type: 'incremented_age' });
  }

  function handleInputChange(e) {
    dispatch({
      type: 'changed_name',
      nextName: e.target.value
    }); 
  }

  return (
    <>
      <input
        value={state.name}
        onChange={handleInputChange}
      />
      <button onClick={handleButtonClick}>
        Increment age
      </button>
      <p>Hello, {state.name}. You are {state.age}.</p>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image description

This is a form where the age is incremented when a user clicks on the "Increment age" button, and the name is displayed as a user enters it in the form.

Now, let's break this down.

Reducer Function

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}
Enter fullscreen mode Exit fullscreen mode

The reducer function takes in state and action, representing the current state and the type of action the user has "chosen," respectively. It returns the next state depending on the action type, following the convention of using switch for readability.
In this example, when the "Increment age" button is clicked, and incremented_age is called, the reducer function returns this object: { name: state.name, age: state.age + 1 };

According to the reference, it's recommended to wrap each case block in { and } curly braces to prevent variables declared inside different cases from clashing with each other.

Additionally, it's important to note that state is immutable, meaning we should avoid modifying any objects or arrays within it. To achieve this, a spread operator is used to create a shallow copy.

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // Return a new object
      return {
        ...state, // a spread operator
        age: state.age + 1
      };
    }
Enter fullscreen mode Exit fullscreen mode

useReducer()

const initialState = { name: 'Taylor', age: 42 };

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

OR

  const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });
Enter fullscreen mode Exit fullscreen mode

state: the current state with the initial value of { name: 'Taylor', age: 42 }
dispatch: a function called depending on the user's action

When a user enters a new name:

function handleInputChange(e) {
  dispatch({
    type: 'changed_name',
    nextName: e.target.value
  });
}

<input
  value={state.name}
  onChange={handleInputChange}
/>
Enter fullscreen mode Exit fullscreen mode

When a user clicks on the button:

function handleButtonClick() {
  dispatch({ type: 'incremented_age' });
}

<button onClick={handleButtonClick}>
  Increment age
</button>
Enter fullscreen mode Exit fullscreen mode

React will:

  1. Store the next state
  2. Render your component with it
  3. Update the UI

Whether we should use the useState or the useReducer is discussed here:
Comparing useState and useReducer

This blog focuses on the usage of useReducer and useContext, so I'll skip that discussion.

Now that I understand the useReducer, it's time to explore why using both useReducer and useContext can be dynamic.

Top comments (3)

Collapse
 
brense profile image
Rense Bakker

You don't have to write a reducer function with switch cases, if you simply want to update properties of an object, you can just have a reducer function that takes a partial object and spread it onto the existing state, no need to create switch cases for each separate property.

Collapse
 
nishantbhavsar001 profile image
Nishant Bhavsar • Edited

ya you are right but this is not a good approach to writing code, this approach will break the code readability for other developer, ninja coding is good to see but in case of code readability ninja coding make reviewer crazy so as a good developer we have to write a readable code or in this useReducer example we can do as you asked in your comment but if we not use type or switch case code reviewer not able to under stand what kind of operation we are performing so this is good approach to write code in useReducer using switch case and type or type must be string which related to operation we are performing so code reviewer can understand by reading that string type
thank you

Collapse
 
shubhadip_bhowmik profile image
Shubhadip Bhowmik

πŸŽ‰ Ah, the marvels of useReducer in the React realm! It's like having a super-powered Swiss Army knife for state management. But let's be honest, diving into useReducer can feel like wandering into a labyrinth where the Minotaur is just type coercion trying to mess with our state! 🀯


From useState to useEffect, we've swirled through React's Hooks carousel, but lo and behold, useReducer beckons like that hidden level in a game you discover after ages! πŸ˜„

When an empty object and a string start having a complex relationship in JavaScript, you know it's time for the state maestro, useReducer, to step in and untangle the enigma. It's like solving a puzzle where the pieces keep changing shape but somehow, the reducer function knows the secret dance steps to make it all work! πŸ’ƒπŸ•Ί

That code snippet with the incrementing age and changing name feels like a coding playground, doesn't it? Click a button, change a name, and voila! It's the state-changing theater of dreams brought to life by the magical useReducer spell. ✨✨

The reducer function is our guiding light, sorting through actions and returning states like a conductor leading a symphony. And remember, folks, state is sacred, so let's keep it immutable and avoid those in-place changes! πŸ›‘οΈ

Now, integrating useContext with useReducer? It's like inviting the Avengers to a tea party - things are about to get dynamic and downright awesome! Together, they're the dynamic duo, ready to tackle the complexities of state like true superheroes. πŸ¦Έβ€β™‚οΈπŸ¦Έβ€β™€οΈ

So here's to diving deeper into the React ocean, exploring the mysteries of useReducer, and coming out on the other side with a state management superpower! Onwards to dynamic, efficient, and oh-so-awesome code! πŸš€πŸ’»

Happy coding adventures,
Shubhadip Bhowmik

Image description