Post thumbnail

Update state with useReducer synchronously

reactquick tip

Hooks in Reacts are wonderful to work with and managing local state is one of the most common ones. For more complex states the useState() hook could be changed to a useReducer() instead. A problem we might encounter is that state isn't updated synchronously after we've updated it.

Let's say we have this simplified useReducer just updating either first name or last name in a state object.

const initialValue = {
  firstName: 'Bobby',
  lastName: 'Andersson',
};

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'firstName':
      return { ...state, firstName: action.payload };
    case 'lastName':
      return { ...state, lastName: action.payload };
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(reducer, initialValue);

So every time we call the dispatch function with a type of either firstName or lastName the state object get's updated.

So if we would do the following the the output in the console would be Erik, right?

dispatch({ type: 'firstName', payload: 'Erik' });
console.log(state.firstName);

Wrong! It would still be Bobby output on state.firstName even though we changed first name to Erik at the previous line. How strange 🤔

The reason is that this is an asynchronous function that is returning a new state instead of mutating the existing one. So when we are logging to console, which is a side effect, the new state actually hasn't been updated yet. If we do another dispatch with a new name it will output Erik that time, it will always be one behind.

A simple solution to this is using useEffect for whenever state is updated and put our logic in that function instead.

dispatch({ type: 'firstName', payload: 'Erik' });

useEffect(() => {
  console.log(state.firstName);
}, [state]);

This way we will always get the correct state logged to our console.