How to use useReducer hook to mimic setState method?
Tags: react, javascript
Published: 2019-06-26 20:56:59

When using class components you can use `setState` method to update **only part** of the state object. React will merge updated value with the previous value of the state object:

```jsx
//Somewhere in CLASS COMPONENT

//set initial state:
state = {
  name: 'Bob',
  age: 25,
};

//update name value with setState method:
this.setState({ name: 'Alice' });

//updated state:
state = {
  name: 'Alice',
  age: 25,
};
```

Now let's try to do the same thing with `useState` hook and function component:

```jsx
//Somewhere in FUNCTION COMPONENT

//set initial state:
const [state, setState] = useState({ name: 'Bob', age: 25 });

//update name value with setState updater function
setState({ name: 'Alice' });

//updated state:
state = {
  name: 'Alice',
};
```

As you can see we "lost" information about age. `setState` didn't just update name property. It returned new state object that was passed to it.

One way to solve this problem is to use `setState` with the callback function:

```jsx
//Somewhere in FUNCTION COMPONENT

//set initial state:
const [state, setState] = useState({ name: 'Bob', age: 25 });

//update name value with setState and callback function
setState(prevState => ({ ...prevState, name: 'Alice' }));

//updated state:
state = {
  name: 'Alice',
  age: 25,
};
```

This works perfectly fine. But... if you want to use terse version of `this.setState` method in function component (or just have some fun) you can use `useReducer` hook to do it.

## useReducer like setState method

First let's write some pseudo code:

```jsx
//Somewhere in FUNCTION COMPONENT

const reducer = (prevState, updatedProperty) => ({
  ...prevState,
  ...updatedProperty,
});

const initState = {
  name: 'Bob',
  age: 25,
};

//initialize state with initState
const [state, setState] = useReducer(reducer, initState);

//update name value (like we do in class component!)
setState({ name: 'Alice' });

//updated state:
state = {
  name: 'Alice',
  age: 25,
};
```

This time it worked as expected. We've updated name value and didn't lose age property.

And now full working example:

```jsx
import React, { useReducer, useEffect } from 'react';

const reducer = (prevState, updatedProperty) => ({
  ...prevState,
  ...updatedProperty,
});

const initState = {
  name: 'Bob',
  age: 25,
  isLoading: true,
};

function App() {
  const [state, setState] = useReducer(reducer, initState);
  
  const handleOnChange = (e) => setState({ [e.target.name]: e.target.value });
  
  useEffect(() => {
    setState({ isLoading: false });
  }, []);
  
  const { name, age, isLoading } = state;

  return(
    <>
      {isLoading ? 'Loading...' : (
        <>
          <input type="text" name="name" value={name} onChange={handleOnChange} />
          <input type="text" name="age" value={age} onChange={handleOnChange} />
        </>
      )}
    </>
  );
}
```

And now the most important question:

Do you **have to** use `useReducer` here?

No!

Can you?

Yes! If you like :)

This post has been inspired by [Kent C. Dodds](https://dev.to/kentcdodds)' course "[Simplify React Apps with React Hooks](https://egghead.io/courses/simplify-react-apps-with-react-hooks)".