I'm sure you've heard of Redux so far, and maybe you've come across articles like "You Don't Need Redux", "When to Use Redux", or "Why you don't need Redux". As someone who actively uses Redux and enjoys writing about development, I feel it's only right to dedicate an article to this powerful, not dead and not outdated library.

Redux is a popular state management library. It's most commonly associated with React, but it can be used with any other framework.

Critics argue that Redux introduces unnecessary overhead or that we should Just use Context and hooks instead. After all, React has it's own state management and context api.

Another common critique is that Redux adds complexity to projects.

All valid arguments. And honestly, Redux really isn’t necessary for simple apps. But from my experience, simple apps tend to grow into complex beasts.

I've tried using React's state management exclusively. It turned out to be a disaster when shared state gets more complex. Maybe I'm not that good at it, it's a possibility.

Why Redux still works for me

It's Opinionated

Opinionated is good as long as you agree with the opinion, and I do in this case. Redux has it's own way of handling state via stores, actions and reducers.

Store

Store is a centralized state container for the state that needs to be shared across different parts of your application. To make the store modular and manageable, usually you'll see a state consists of slices. Slices are just as the name says, little pieces that make sense on their own, instead of managing one large store.

Action

Action is a plain javascript object that describes what should happen to the state of the store. Action is used by a reducer.

Reducer

Reducer is a pure javascript function that accepts store and an action and returns a new state

redux flow diagram
Flow: store passes action to reducer, reducer returns new state, store updates state, subscribers are notified

State Update Flow is Predictable

Redux keeps all your app's data in one place, so you always know where it is and how it's being updated. When something changes, Redux uses simple, predictable functions (called reducers) to decide exactly how the data should be updated. This makes it easy to see what happened, step by step, and understand how your app reached its current state.

Redux Devtools is great for debugging

Go on and install that Chrome extension, it's awesome! With Redux DevTools, you can view dispatched actions, the state before and after each action, and even inspect the raw payload of the actions. That's what I use most of the time.

Of course, you can also go back in time, rewind and replay previous states. Personally, I can count on one hand the number of times I’ve used it in practice. It's good that it's there though.

redux devtool chrome extension is great for debugging
redux devtools chrome extension

Comparing Redux to ContextAPI

In Context API you have the freedom to organize your state logic however you want. I'm not sure if this is always a good thing, as flexibility opens a door to chaos.

You could, of course, implement a redux-like Context api:

import React from 'react';

const StoreContext = React.createContext(null);
const initialState = {
  loggedIn: false,
  user: undefined,
};

export const StoreProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer((state, action) => {
    switch (action.type) {
      case 'LOGIN':
        return { ...state, loggedIn: true, user: action.payload };
      case 'LOGOUT':
        return { ...state, loggedIn: false, user: undefined };
      default:
        return state;
    }
  }, initialState);

  return <StoreContext.Provider value={{ state, dispatch }}>{children}</StoreContext.Provider>;
};

export const useStore = () => {
  const context = React.useContext(StoreContext);
  if (!context) {
    throw new Error('useStore must be used within a StoreProvider');
  }
  return context;
};

And then in your component:

const { state, dispatch } = useStore()
dispatch({type: 'LOGIN', payload: {name: 'John Doe'}})

You have a central state, dispatch and actions without needing to install Redux.

I still prefer to use Redux because I'd need to lose time to make strictly typed things, and I also wouldn't have such a nice debugger tool like redux devtool. I'd want to easily include the middleware. I'd need to make some things on my own and lose my development time. But yeah, I'd have few dependencies less and a smaller build size.

What I'd like to recommend when using Redux

  • Don't use generic action names - bad readability when debugging. Better to name them in a way that you can easily connect an action to the reducer. If an action is on user reducer, it might be updateProfile etc.
  • Use unique action names. If you don't use a unique you might end up unintentionally dispatching an action that will be handled by multiple reducers. Been there, it's hard to debug. If it's intentional, carry on.
  • Test your reducers. They're perfect for unit testing, being pure functions and all. If not, well it's grounds for bugs along the way.
  • Don't put everything in redux. Not all state needs to live in redux. Your business logic might need to. I'm not sure I'd put modal opens/closes in Redux. I'd probably put it in useState for local, or use context api for global modals.
  • Don't overcomplicate your reducers. Reducers should be pure functions, not mutations. For mutations use something else, maybe redux thunk. If you do use a mutation, you lose reducer predictability and complicate debugging and bear in mind that doing too much in a reducer might end up with blocking.
  • Doing too much in the reducer blocks state updates, making the app sluggish. A reducer should focus on updating a single slice of state.
  • Use middlewares - Middleware's are great in developer mode. You can profile your actions, log them, whatever...
  • Use redux devtools - did I say already how great it is?

The Context API Has Its Place

I do use Context API alongside Redux. While Redux offers structure and scalability, Context provides flexibility. I would use Context API for anything that isn't business logic, such as managing UI preferences, theme toggles, etc. To sum it up, I'd say: Context API for presentation logic, Redux for business logic.