Reducers in React

What the heck is a Reducer and why do I need to know about it?

You're probably already familiar with Contexts in React, we use them with useState and useEffect to keep track of session data, such as who is logged in, if they have anything in their shopping cart etc.

We use actions and unique strings which may or may not contain a payload. We might need some examples to make this a little easier to understand. Stick with me here. For now, we need to know that reducers are object representations of values that receive actions. The actions update values within the reducer object.

Step back, away from the ledge!

Taking a step back, if we consider Contexts in React, we might use a Context to hold values like who is logged in, what they have in their cart etc. A typical application might look like this:

Application in React

Users needs to know about any users that sign-in, Products needs to know about any products we have available and Shopping Cart needs to know if anything is added to the cart and what needs to be passed over to the Checkout.

That's all pretty clear and simple, so how does the checkout know who is doing the shopping, and how might we do things like targetted deals, such as 'all our customers from the UK receive a banner add offering them a discount if they use a coupon code specific to the UK'?

We could of course build everything in a single JS file, but it would be difficult to maintain and make it difficult to re-use code. If you consider something like an ecommerce site for example, they're all kind of the same, they have different UI's and different products, but they all list products, they let users login, they sell products.

So designing your code in a reusable way, using components for individual parts of the site, allows you to not only port those components to other ecommerce sites, but also to focus any improvments or trouble-shooting to specific components rather than the entire site. So we do want to break our code up into components.

This brings us back to the question, how do we let all the components that need to know about a user, actually know about the user?

We can do this in a few ways, the React Context way is one that we probably learn first. It allows us to pass data through the component tree without using props at every level. A context might look a little like this:

React Context

A Reducer is similar to the useState hook in that it allows you to store session data or custom state logic. We might consider using Reducers when we find that we're having to track multiple piece of state data that may rely on complex logic.

Reducers are pretty simple really;

export const USER_ACTION_TYPES = {
  SET_CURRENT_USER: 'SET_CURRENT_USER',
};

const INITIAL_STATE = {
  currentUser: null,
};

const userReducer = (state, action) => {
  const { type, payload } = action;

  switch (type) {
    case USER_ACTION_TYPES.SET_CURRENT_USER:
      return { ...state, currentUser: payload };
    default:
      throw new Error(`Unhandled type ${type} in userReducer`);
  }
};

Reducers are functions that return a new object, React works by re-rendering new objects, that said it does seem to be a more convoluted way of doing things so why bother?

Well, for a context this simple, we don't want to use Reducers, useContext is a more suitable option. However, as the number of value states that we want to change grows, then Reducers make more sense. Reducers as a learning topic is generally considered to be a difficult feature to learn and I completely agree with this.