Have you had a conversation about Redux recently that has left you wondering — despite all of its praise and the assured recommendations — if you made a big mistake when you typed the words,
yarn add redux?
Just got home from the React thought leader meeting. Bad news, we apparently all hate Redux now so please start rewriting your app as soon as convenient, thanks
— Jani Eväkallio (@jevakallio) February 12, 2018
Jani is being a bit facetious here. But if you’ve been following along on twitter, there has been a lot of discussion about redux. The crux of the conversation is aimed at whether most apps even call for the use of redux and consequently if we, as developers, are using it when we don’t need it.
Realization: Putting Redux in our company framework by default was a mistake.
1 People connect *every* component.
2 People embed Redux in "reusable" components.
Everyone uses Redux. Even when they don't need it.
4 People don't know how to build an app with just React.
— Cory House ? (@housecor) February 11, 2018
I see this sentiment time and time again. We’ve been presented with this powerful tool and set of patterns for managing state in our apps. Then with wild abandon we plaster our entire app with reducers, actions, connected components, and tangled dispatch functions. Once you’ve mastered the Redux flow, everything starts to look like global state.
In my mind the conversation boils down to this: we tend to overuse Redux and that has some consequences for our apps and how we build them. I’ve had my share of moments staring at the beginnings of a component wondering if component state will suffice or if I might as well wire it up to Redux seeing as most everything else is. I’ve also spent plenty of time navigating between the reducer file, action file, container, and presentational component wondering what all the ceremony is getting me besides cramped fingers.
So, let’s leave the thought-leadery platitudes behind and (as Scott Rogowsky would say) get down to the nitty gritty.
When should we use Redux?
In the midst of a chorus of “don’t use Redux unless you need it”, “don’t use Redux for everything”, and “only pull in Redux once you feel the pain of managing state without it”, it is easy to be left scratching our heads wondering, “well, geez, I was told how great this library is, but I’m not sure if I’m even supposed to be using it.”
It’d be helpful to have a set of guidelines. I think Ken Wheeler gets us pointed in the right direction.
FWIW, I think a lot of it comes from people using it for EVERYTHING. My personal state preferences:
Server data: Abstracted away or via something like a graphql client where it's managed.
Form state: setState
Local state: setState
Global state: Redux or Mobx or Unstated
If we have a component, such as a toggle element, that has some basic state that only it cares about, keep that in component state. Similarly, when building out an interactive form, we can treat the parent form component as the arbiter of the state of the form. Component state updated with
setState() will again suffice.
The point at which we want to start thinking about a more managed solution, like Redux, is when that state is global. Most of our state probably isn’t global though. Which brings us to an important take away: we can and should use both local and global state in our apps. Managing everything in Redux is overkill. It may have negative performance implications, it will increase the complexity of your app, make it hard to refactor, and likely reduce the reusability of many of your components.
So if we ought to only use Redux for global state, what do we consider to be global state?
Most common example: I see people using Redux to hold form values.
Rule: If only a single component cares about the data, use local state. https://t.co/9LVAkBwNvN
— Cory House ? (@housecor) February 12, 2018
Let’s invert this rule from Cory House. “If more than one component cares about the data, use global state.”
Before we get carried away let’s expand on this a bit more. Technically speaking, people were build big fancy complex React apps before Redux came along. When the same piece of state was needed in disparate parts of the app, they just pushed the state up and up into some parent component until that state could flow down to wherever it was needed. This works. But it can be unpleasant. Component after component has to pass pieces of the state that it doesn’t care about down because some great-great-grandchild needs it. This situation has a name — prop drilling.
This is the situation where we want to bring in Redux.
Let’s make it a bit more concrete. Assume we are building an e-commerce site. We have a checkout page that shows the items we have purchased with their respective prices and the total shopping cart price. We manage all of this state with local component state in the
Checkout component. Then, we go to implement the
NavBar component which shows the number of items in the cart and the total shopping cart price. We quickly realize not only that we already have this data managed and rendered in the
Checkout component, but also that we want both the
Checkout component and
NavBar component to stay in sync as the contents of the cart change. In other words, we have two disparate components that need to read and possibly update the same state.
This is a perfect time to pull the shopping cart state of our
Checkout component up into Redux. Once this data is in Redux, the
NavBar components can individually connect to Redux with the state and dispatch functions they need.
We can assume that our e-commerce site has a bunch of other React-based functionality all of which at this point is managed in local component state. Leave it as is! There is no need to rewrite any of the existing codebase with Redux. If you’re feeling the urge to go on a Redux-ification spree, stifle it. Whatever development-time savings you were able to promise with the introduction of Redux will soon vanish with that ill-conceived refactoring.
Redux is a powerful tool. It is most powerful if we know when to use it and we have a grasp of what our other options are. Our apps, especially SPAs, can get really complex really quickly. Much of our app state is local state and as such ought to be managed by a component. Once a slice of our state is needed in disparate components — components in distinct component hierarchies or components that are more than two levels apart — it may be time to bring in Redux.
It is worth noting that there are many other options for state management in the React ecosystem. As mentioned above, it can be perfectly reasonable to manage state with nothing more than
setState(). A popular alternative to Redux is Mob. Certainly there are many others. This whole conversation is further complicated by the introduction of the Context API in React 16.3.0 as a formalized, first-class feature. Find an approach and tool that works for you, your team, and the particulars of your project.
There is no perfect tool / framework / language / process. All software is created within a context, and trade-offs are made based on that context.
Learning to see and evaluate technical decisions from this angle will help you ask better questions, and build better systems
— Caitie McCaffrey (@caitie) February 19, 2018