useState vs. useReducer

Rachel Reilly
2 min readFeb 8, 2021

--

A screenshot of a loan calculator that uses the useReducer hook to control state management

When I was in bootcamp, we learned to write React with class components and context for global state. I was never introduced to Redux or reducers. It wasn’t until I made my first game that I felt the need to use some more robust state management.

I love the useState hook, but there are some benefits to having the state contained in one object. With individual pieces of state, things can get out of sync. In One Dimensional Chess, the game where I first used the useReducer hook, I had one piece of state that controlled the current board and one piece that kept track of the current team. This was a problem because the team would change if the user successfully made a valid move for the selected piece, but if the move was invalid, the team wouldn’t change.

The React documentation says that useReducer is preferable to useState “…when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.” That is a pretty low bar to use the useReducer hook instead of regular functional component state. In fact, almost all global state meets one of those qualifications.

Last week, I was working on a simple loan calculator. Rather than making the user press a submit button for the form, I wanted all the input values to be controlled through state so the payment info would update immediately on change.

When I was planning this project, my gut told me to use a reducer, and I talked myself out of it by saying that controlling a form with a reducer is overkill. The state for the loan calculator only included three values: three strings from controlled form input and a boolean.

Near the end of the project, I was making a toggle button that allowed the user to calculate their loan term in months or years. I created this as a sub-value of the loan term, and I had convoluted setter functions setting a sub-value of state and copying the other sub-value.

I was no longer passing the setStateName() functions through the context, I was passing down custom functions that validated the input before setting the state. Then it hit me: the logic of my project fit best into a reducer. I quickly refactored the project to use a reducer by copying the functions I was already using and pasting them into the dispatch cases.

--

--

Rachel Reilly

Software Engineer specializing in React but passionate about the future of digital interaction beyond visual user interfaces