DEV Community

Mihir Chhatre
Mihir Chhatre

Posted on

React useReducer() 🪝

useReducer() can serve as a replacement for useState() in scenarios where more powerful state management is required.

useState() is the primary state management hook in React and it works well when state updates are simple and limited to a handful of updates. useReducer() works better when you have related pieces of data/state.

Below is the syntax for calling the Reducer hook:
const [state, dispatchFn] = useReducer(reducerFn, initialState, initialFn)

useReducer returns an array. The first element within this array is a state snapshot used in the component re-render cycle. The second element is a function that can be used to dispatch a new action(trigger an update of the state).

useReducer() takes 3 arguments. reducerFn is a function that is triggered automatically once an action is dispatched(via dispatchFn). It receives the latest state snapshot and returns the new, updated state. The second argument is the initial state (this is usually an object). initFn is a function to set the initial state programmatically.

Let's jump into some code to better understand these concepts


⚔️ useState() vs useReducer() ⚔️


1. Building with useState()

The code below creates an application where clicking on the button fetches 'posts' from a JSONPlaceholder and updates the states(there are three states - loading, post, error).

import React, {useState} from 'react'

function App() {

  const [loading, setLoading] = useState(false)
  const [post, setPost] = useState({})
  const [error, setError] = useState(false)

  const handleRequest = () => {
    setLoading(true)
    setError(false)
    fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      setPost(data)
      setLoading(false)
    })
    .catch((err) => {
      setError(true)
      setLoading(false)
    })
  }
  return (
    <div>
      <button onClick={handleRequest}>
        {loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{post?.title}</p>
      <span>{error && "Something went wrong!"}</span>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode



Let's first understand the code.

Inside the return block, we are displaying the post title and an appropriate message if there is an error.

Within handleRequest, the loading state is set to true. If the request is successful, I am updating the post state and setting the loading state as false. If the request failed, we are setting the error state as true and loading state as false.

The above example works as expected but on closer inspection you may notice how multiple states are updated together within a single function. Often, in such scenarios, replacing useState() with useReducer() is the preferred approach since we are updating multiple states together.

2. Building with useReducer()

Let's now try understanding how to implement the above application using useReducer().

import React, {useReducer} from 'react'

const postReducer = (state,action) => {
  switch(action.type){
    case "FETCH_START": return{
      laoding: true,
      error: false,
      post: {}
    }
    case "FETCH_SUCCESS": return{
      loading: false,
      error: false,
      post: action.payload
    }
    case "FETCH_ERROR": return{
      loading: false,
      error: true,
      post: {}
    }
    default: return state
  }    
}

const INITIAL_STATE = {
  loading: false,
  post: {},
  error: false,
}

function App() {

  const [state, dispatch] = useReducer(postReducer, INITIAL_STATE)

  const handleRequest = () => {
    dispatch({type:"FETCH_START"})
    fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      dispatch({type:"FETCH_SUCCESS", payload:data})
    })
    .catch((err) => {
      dispatch({type:"FETCH_ERROR"})
    })
  }
  return (
    <div>
      <button onClick={handleRequest}>
        {state.loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{state.post?.title}</p>
      <span>{state.error && "Something went wrong!"}</span>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

postReducer is the reducer function that takes two arguments, state and action. state refers to the current state while action is used to pass information from the component to the reducer function. There are be three kinds of information that we would want to pass -

Case 1 : When the button is clicked, the component informs the reducer that a fetch request is initiated. The reducer then updates the state as follows:

loading - true
post - {}
error - false

Case 2: If is JSON is fetched successfully, the component informs the the reducer and shares the response object. The reducer updates the state as follows:

loading - false
post - {id:1, title: "some_title, desc: some_description}"
error - false

Case 3: If there is an error in fetching the JSON, the component passes this information to the reducer. The reducer then updates the state as follows:

loading - false
post - {}
error - true

The above example illustrates perfectly how useReducer() makes the complex multiple state updation process seamless. 👌.

The reducer hook provides the ability to perform declarative state updates by decoupling state updates from the action that triggered the update.



The dispatch function allows us to send actions to the reducer. The reducer function essentially updates the state and returns the updated version of the state.

If you observe closely, the reducer function is written outside the scope of the component function. The reason behind this is because the Reducer function(postReducer) does not need to interact with anything defined within the component function. All information required inside the reducer function will be passed by React automatically.


It's completely understandable if the reducer hook got you feeling like this 👇



useReducer() tends to be a complex hook to grasp but understanding its functionality will go a long way when learning Redux.

Top comments (2)

Collapse
 
jegangits profile image
jegan
Collapse
 
mihir_chhatre profile image
Mihir Chhatre

Glad it helped :')