Skip to content

· 3 min read

Taming the useState Hell: An Introduction to useReducer in React

Also on Medium

I was working on my most recent project, a platform for scheduling decentralized streamings, and as the name “schedule” implies, you have to enter dates, titles, amount of participants, and so much more.

So I created my component for this form, let’s call it “ScheduleForm,” and of course the question “how am I going to manage the state of this form” emerged. The answer was simple, because it was only local state, I used useState, but I ended up with something like this.

But this code made me think.

“How can I tell whether the end date is before the start date?”

“How can I know if my title is too long?”

“Do I need to write a function for each form checker? “How can I handle errors?”

Sure, we could hold our breath and hope that everytime we call set*() we would remember (or even know) to validate all of these things before writing to state, but I don't sleep well knowing that things are just sitting in such an easily broken state.

Of course, the code above has an obvious refactor.

but sadly, it has the same problems, just lesser code

Here’s where the useReducer comes into play

useReducer It’s a more complex method of handling state in React that’s a powerful alternative to useState. The useReducer hook accepts two arguments: a reducer function and a starting state. The reducer function takes the current state and an action as input and outputs a new state. This allows for the clean and systematic management of complicated state changes and interactions between various state variables.

So, the code above can be refactored as follows:

You may be thinking, “Jesus, this is still the same as the code above,” and you are correct in one aspect.

You see, while the above format hopes that you always remember to spread on ...scheduleValues so you don't mess up by mutating the object directly (causing React to not re-render as expected), it still misses out on the most important benefit of useReducer which is the ability to supply a function that controls state transitions.

Returning to ‘useReducer,’ the only difference is that you get an extra argument, which is a function that can assist us ensure that each state transition is safe and valid:

This has the big advantage of ensuring that your state is always valid in a fully centralized manner.

So, even if additional code is introduced in the future, and future developers on your team call updateEvent() with potentially erroneous data, your callback will always trigger.

For example, we may wish to always ensure that the end date is never before the start date (since this would be illogical), and that the title has a maximum length of 100 characters:

Having this function makes a HUGE difference when you need to validate data, handle errors and making everything cleaner and more scalable through time since prevent direct mutation of state.

When should i use which

The choice between useState and useReducer depends on the complexity of your state management and the size of your application.

useState is best suited for managing simple state values, such as toggle switches, input fields, or counter values. If your state management only involves a few values that are simple to update and don't have complex interactions with each other, useState is a great choice.

useReducer, on the other hand, is best suited for more complex state management, such as data management, form submissions, or large-scale state updates. If your state management involves multiple state values that have complex interactions with each other, or if your application is growing in size and complexity, it may be a good idea to use useReducer.

It’s also worth noting that you can use both useState and useReducer in the same application. For example, you can use useState to manage simple state values, while using useReducer to manage complex state interactions between multiple state values.

In general, useState is a great starting point for state management in React, but as your application grows, you may find that useReducer provides a better solution for managing complex state.

This is just short intro to useReducer world, than im planning to expand a future post :)