React: A Journey from useState to useReducer hook

Mahesh
JavaScript in Plain English
4 min readAug 14, 2023

--

Intro

Welcome to an expedition where we transition from the React’s useState hook to building our very own useState and then we will delve into the intricacies of crafting our own useReducer implementation, before circling back to the reliable useReducer hook that React has in store.

The Motivation

We’re on a quest to unravel the inner workings of these hooks and to foster a comprehensive understanding, uncovering their shared essence while appreciating the nuances that set them apart.

The App

In this blog post, we’ll construct an uncomplicated counter app, strategically chosen to keep us laser-focused on our objectives: comprehending the nuances of useState and useReducers.

fig(a): counter app

You have two options: either build your own application using Create React App (CRA) and seamlessly substitute the provided index.jscontent, or effortlessly follow along using our shared code sandbox. The CounterState component renders a sum and a button to update the sum by one. Below is the starter code for our counter component.

a. useState

Here we will make use of the useState hook provided by react library and pass in an initial value of zero. As per this, it returns a tuple where the first element of a tuple is the state and the second element is a function to update the state.

Upon clicking a button, our handler function makes use of setSum to update the state value by one. Refer `CounterState1` component in the provided CodeSandbox for a comprehensive implementation of this section of the illustration

b. useState (step towards custom hook )

Now, our attention shifts towards crafting a personalized version of the useState functionality. For the first phase we will reference React.useState within our custom useState to provide us with the state and function to update the state.

Our counter app should still behave the same. Refer `CounterState2` component in the provided CodeSandbox for a comprehensive implementation of this section of the illustration.

c. useState (custom implementation)

Our personalized useState will continue to take an initial value, establishing this value as the state for the initial encounter. Subsequently, our state update function will welcome new state values, seamlessly replacing the existing/current state with the freshly provided one.

While React handles the task of rendering the DOM upon state updates, our self-implemented custom useState unfortunately forfeits this capability. To address this, a straightforward approach to ensure re-rendering is to invoke the render function after state modification.

And that concludes the fundamental implementation of the useState hook. Refer `CounterState3` component in the provided CodeSandbox for a comprehensive implementation of this section of the illustration.

d. useState ( implement useReducer )

Our objective at this stage is to extract the direct access to the state-setting function from the consuming application. Instead of delivering a state-setting function as the second item in the tuple, we will provide a dispatch function. To begin with, the updated state will be transmitted using the dispatch function. However, the dispatch function itself does not possess the capability to directly update the state; rather, it relies on a predefined intermediary function known as the reducer function to perform state updates.

In our example, the reducer function currently performs a simple task of returning the received value, but we will expand on this concept in the subsequent part of the blog.

Our component should be making use of a dispatch function to send the updated state. So the code should look like this,

Let’s enhance our reducer function with a more detailed approach. This adjustment ensures that the dispatch function no longer directly modifies the state. Instead, it emits a message, triggering the reducer function to perform the state update. Through this approach, we effectively detach the direct state manipulation within the consuming components.

And that concludes the fundamental implementation of the useReducer hook. Refer `CounterState4` component in the provided CodeSandbox for a comprehensive implementation of this section of the illustration.

e.useReducer

Finally, we will refine our custom implementation and revert to utilizing the useReducer hook offered by the React library. However, we will ensure the continued utilization of the dispatch and reducer function/signatures that we established throughout this process.

Refer `CounterState5` component in the provided CodeSandbox for a comprehensive implementation of this section of the illustration.

Conclusion

In this journey, we commenced with React’s useState hook, ventured into building our customized version of useState, and subsequently transitioned to our personalized useReducer implementation. Ultimately, we circled back to utilizing React’s native useReducer. This exploration has provided us with a profound understanding of these essential tools and their intricate interplay.

My previous three blogs:
1. JavaScript Basics for Coding Challenges
2. React: Building dropdown component
3. React: SPA with path-based routing in AWS Cloud

In Plain English

Thank you for being a part of our community! Before you go:

--

--