DEV Community

Cover image for useState vs useReducer ultimate guide
Matheus Costa
Matheus Costa

Posted on

useState vs useReducer ultimate guide

Are you writing complex functions on useState? You should probably use useReducer instead.

useState vs useReducer

First things first, what is the difference between useState and useReducer? It's simple, actually. useState returns a state and a function which you'll use to update the state, while receiving an optional argument to initiate the state with some starting value.

const [state, setState] = useState('optional value');
Enter fullscreen mode Exit fullscreen mode

useReducer returns a state and a dispatch function, while receiving a reducer and an initial state.

const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

What is a reducer?

A reducer is a pure function which has two parameters, a state and an action (now we'll understand the dispatch from useReducer). While we use the state to store data, the action will be an object with a type and a payload properties. We'll use the type to identify the dispatched action, and the payload to update the state.

const initialState = ['javascript'];

const ADD_TECHNOLOGY = 'ADD_TECHNOLOGY' // for error handling purposes, we define the action like this

function reducer(state, action) {
  if(action.type === ADD_TECHNOLOGY) {
   return [action.payload, ...state]
 }
}

const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

And why should you stop using useState for complex functions?

Mostly, performance and separation of concerns. Sometimes your application is just too small and you probably can get away using just useState, but then it starts growing and you need to separate the components from the state. You let the reducer take care of the state while the component just reacts to it. It gets easier to unit test because you don't need to mount the entire component, and saves you lots of unwanted re-renders.

Bad example:

Imagine you have two components, the Application one and the Technology one, which will have the technology information. For each technology in the state, we'll render a Technology component.

const [technologies, setTechnologies] = useState(['javascript']);

const addTechnology = technology => setTechnologies([technology, ...technologies])
Enter fullscreen mode Exit fullscreen mode

You find out that your addTechnology is defined in every re-render causing all of the child components to re-render when a new technology is added. For tutorial purposes we're using a simpler scenario but imagine its causing you performance issues. What would you do? useCallback it.

const [technologies, setTechnologies] = useState(['javascript']);

const addTechnology = useCallback(technology => setTechnologies([technology, ...technologies]), [setTechnologies, technologies])
Enter fullscreen mode Exit fullscreen mode

But then you realize it does nothing, cause having to pass the state and the setState function in the dependencies array keeps defining the function at each re-render, causing all the technology components in the tree to re-render in the same way.

Good example:

Now let's try the same refactoring but using useReducer instead:

const initialState = ['javascript'];

const ADD_TECHNOLOGY = 'ADD_TECHNOLOGY' // for error handling purposes, we define the action like this

function reducer(state, action) {
  if(action.type === ADD_TECHNOLOGY) {
   return [action.payload, ...state]
 }
}

const [technologies, dispatch] = useReducer(reducer, initialState);

const addTechnology = useCallback(technology => {
 dispatch({
  type: ADD_TECHNOLOGY,
  payload: technology
 })
}, [dispatch])
Enter fullscreen mode Exit fullscreen mode

As we separated the state from the component, we just need the dispatch on the dependencies array. Now the reducer is taking care of all the state logic, this way only the new Technology component will render, not affecting the previous ones.

Conclusion

There is no silver bullet in programming, so every solution has its own use case. If your application is getting complex and you're having issues with unwanted re-renders, maybe you should try this approach. I hope you find this tutorial useful and reach me out if you have any questions!

Top comments (0)