Redux: when to use it and when to lose it

Some things you want to use a lot of the time (clothes). Some things you want to use sometimes (tire pressure gauge) and somethings barely ever (this hat). I hear you ask: “But what about the popular Javascript state management tool Redux Carly? When do I use that?”

Well, it’s your lucky day because I am here to provide some clarity on when implementing Redux is a good idea and when to skip it. It is a tool that many people have strong opinions on, so let’s dive into it!

If you are unfamiliar, Redux is a Javascript library written by Dan Abramov and Andrew Clark (of React fame). React already has built in state management (useState, useContext etc), but Redux was specifically designed to manage complex state in larger applications, and it can be used outside of React (such as in Angular).

The createStore function is the heavy lifter in the Redux library. From the docs (I added comments):

import { createStore } from 'redux'

//initialize a reducer here
function todos(state = [], action) {
  switch (action.type) {
    //initialize some state 
    case 'ADD_TODO':
      return state.concat([action.text])
      return state

//initialize the store  
const store = createStore(todos, ['Use Redux'])

// update the store
  type: 'ADD_TODO',
  text: 'Read the docs'

// [ 'Use Redux', 'Read the docs' ]

The createStore function takes three arguments: a reducer function, the initial state, and any store enhancer functions. The reducer function is intended to return the next state tree, which is rendered relative to the supplied action and state. The state tree which is returned is an object called the Store. The enhancer function (or functions) are intended to allow for the utilization of various middleware tools.

So, similar to useReducer, the way to change/affect the Store is by dispatching an action onto it. However, unlike useReducer, this is the only way to affect the state in the Store. By doing this Redux encourages very strongly directed flow of information in the app, enforcing that all state changes are directed to the global Store using the global dispatch function.

But, you might be saying, “But what if I just want to move my useReducer up into the top level of my app? How is that different from Redux?” I mean, that is essentially how Redux works. However, with Redux you have a clear pathway for unidirectional state flow at a global level in your app out of the box, in addition to the ability to implement lots of cool middleware like the action-logger, which helps you debug complex state. So if you have a need for predictable, clearly enforced data flow in a complex app, don’t reinvent the wheel, and try out Redux.

However, there are some tradeoffs. Dan Abramov himself says that the library is “not designed to be the most performant, or the most concise way of writing mutations. Its focus is on making the code predictable.” Its intent is to help engineers untangle complicated, shared state in complex applications. If you are writing an app with relatively simple state, then Redux is likely entirely unnecessary.

Ultimately, useReducer, useContext, and Redux can all be used in conjunction to manage state in large, complex ecosystems, as its intended purpose was to impose predictability and stable, unidirectional dataflow.