useState vs useReducer ultimate guide
Tags: react, javascript, tutorial, webdev
Published: 2022-03-07 21:36:43

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.

```js
const [state, setState] = useState('optional value');
```

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

```js
const [state, dispatch] = useReducer(reducer, initialState);
```

## 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.

```js
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);
```

## 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.

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

const addTechnology = technology => setTechnologies([technology, ...technologies])
```

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.

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

const addTechnology = useCallback(technology => setTechnologies([technology, ...technologies]), [setTechnologies, technologies])
```

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:

```js
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])
```
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!