While researching which flux implementation to use for our team's final project at Hack Reactor, my friend Nick enthusiastically recommended Redux. (Nick is a brilliant front-end engineer who I worked with at Udacity, and is among the most curious and inventive people I know. So when he gets super excited about something, I pay attention.)
If you haven't heard of Redux yet, that's OK. The creator, Dan Abramov, announced it at React-Europe 2015, which, at the time of this writing, was 2 months ago. Was it risky adopting something so new for our team's project? Yes. But the more I learned about Redux, the more obsessed I (and my team) became. And we were not alone.
Goals of this Article
By the end of this article, you should:
- Be able to explain to a friend what Redux is, and why it makes so much sense to use it with React.
- Be able to describe the basic concepts behind Redux and React, particularly in contrast to traditional MVC architecture.
- Know where to go for high-quality resources to get started!
- This is not a tutorial or a code lab. There will be very little, if any, code in this article. I'm working on a Redux/React/Webpack tutorial (follow me on Twitter for updates), and there are a number of great resources listed in the last section, if that's what you're looking for.
- This is not a deep discussion of MV*-whatevers. We can go on and on about the pros and cons (and many have) of various frameworks, but here's the truth. No matter what you choose -- a framework, a library, or nothing at all -- every programmer needs their app to do two things: Display stuff to the user, and track data. That's it. Redux/React is just one way to accomplish those two, age-old tasks.
Assumed Prior Knowledge
I assume that you've heard of React, and ideally have read Thinking in React. If you haven't, you might not get as much out of this article because Redux is really, really brilliant when paired with React. If this is all new to you, it can be hard to tell where React stops and Redux begins (it was for me anyway), so having prior knowledge of React will make these conceptual distinctions more clear.
I also assume that you know what MVC frameworks are, and ideally have used one before. While this prior knowledge is not required to grasp the concepts in this article, it's helpful because part of how I understand and communicate Redux/React is by comparing it to well-known MVC frameworks like Backbone and Angular.
What is Redux?
I'm going to start with an analogy. Imagine you have a room with a sofa and a table, like this:
You want to swap the positions of the sofa and table, and you need it done FAST, so you hire two butlers, Alfred and Rudy, to complete the task at once.
Alfred is in charge of the sofa, and Rudy is in charge of the table. You give them instructions to coordinate, so they aren't crashing furniture into each other.
First, Rudy moves the table out of the way, then Alfred moves the sofa in position.
Next, Rudy moves the table where the sofa used to be. The room now reflects its new desired state.
Super! I've just described a common implementation pattern found in MVC architecture. That wasn't so bad, but let's take it up a notch.
Now imagine you have a GENIE. This Genie provides a completely new instance of the room whenever you want to make a change to anything inside of it. And by "new instance" I mean, unlike Alfred and Rudy, she does not go inside Room A and move stuff around until Room A looks like Room B. She actually creates a new, unique room that is exactly Room B. If that wasn't enough, she also reads your mind. I know, right? It's an excellent setup you've got here.
Instead of giving instructions to multiple butlers about what goes where, all you do is think, Hey Genie, I want this room to be this other room, which right now exists in our shared mind palace.
And POOF. Your Genie returns that new room in real life, so you now have TWO rooms, like this:
The Genie? That's what Redux achieves with React. That's right. THE GENIE IS REAL.
Why is this desirable? Let's say the new furniture configuration is problematic for some reason. Like, maybe the sofa blocks a doorway, which creates suboptimal feng shui, and, also, a fire hazard. Instead of having Alfred and Rudy go back and re-position the furniture to where it was, you can just toss aside Room B, and flip back to Room A.
Or, you can think of a completely different configuration, Room C, and your Genie will return it. If you ever want to revisit Rooms A or B, you can flip back to them. You might think that producing new rooms in this manner is wasteful and slow. It's not. Why? Because she's a motherfucking genie. This is her thing.
Seriously though, right now, it's not important to know why it's fast, just know that it is very, very fast -- in fact, it's faster than the "If this, do that, then do this other thing..." approach embodied in the butlers example.
The capability to create new, immutable states is somewhat grandiosely (or maybe just dorkily) called "time travel" because you can traverse between your app's states. This is what React/Redux gives you. React provides the ability to spin up new rooms, and Redux provides the Genie mind-reader logic behind their production. This logic is very important, and is different from other implementations of a concept called flux, which we'll discuss briefly, next.
OK, What is Redux, Actually?
Basically, flux is like a sedan. It's a concept with many agreed-upon characteristics associated with it, but it's not, itself, a tangible thing. Just as there's no single definitive "Sedan" that you can go out and buy, there's no definitive "Flux" that you can download and install. Instead, you choose one specific implementation of it. I don't know much about cars, but whatever the most minimal, stripped down, yet highly performant sedan is, that's Redux.
The basic idea behind flux is, you have a dispatcher that sends actions to the data store, and updates the view. It's an overall data flow architecture, where -- unlike the two-way data binding of MVCs -- updates only go one way. If you've ever been in getter/setter hell trying to track what view is touching what model, you can understand how a one-way data flow might be beneficial.
Note: This isn't a knock on frameworks like Angular (which I still think is pretty magical) or Backbone (which I... respect as a thing), or any other MVC. In general, I try not to get attached to any one way as the best way for everyone. What constitutes the "best" approach really depends on your application, your use case, and your organization's needs and constraints.
With all that said, here's a helpful diagram from Facebook's Flux documentation:
One huge implication of one-way (or unidirectional) data flow is that your data store is not touched by your view. At all.
Redux is one particular implementation of a subset of flux concepts. The main differences between Redux and full flux implementations are:
- There are no discrete dispatchers in Redux; your store listens directly for actions, and uses a function called a reducer (more on this later) to return a new app state each time an action is dispatched.
- Redux holds your entire application's state in one place.
- Your app's state is immutable.
Consider your app state as a tree, with a root and child nodes. If you want to make a change to a child node, you can't change it directly because state is immutable, so you make a copy of the target node and apply your changes. You also make a copy of the target node's parent, because it wouldn't make sense to have one parent produce two versions of the exact same node. Maybe you append a child node to the new node, too. That's fine. At the end of all this, you just need to attach the existing, unchanged nodes to your new ones. Like this:
The best part is, the original state (the blue one) still exists. The unchanged blue nodes are not re-rendered in the new state, just the orange nodes. This makes debugging really easy because you can isolate the action that caused the state change, and go back and forth at any time. Redux dev tools also happen to be unusually good (see the next section for an example), and, combined with React dev tools, have basically spoiled me for life.
Another implication of this design pattern: You really cut down on the number of dependent asynchronous operations in your data flow. Going back to the room example, recall how you managed the butlers: "Wait for the table to be out of the way, then move sofa..."
Contrast that to the declarative statement: "Sofa and table are positioned at (fill in the x,y coordinates here)." The end. No waiting for Rudy or Alfred or whoever to clear the way first.
Intuitively, this makes sense. If you want a room with a sofa and table in it, positioned in a certain way, then why should it matter the order in which items are placed? This straightforwardness is what really won me over -- it just feels right to me.
How Does Redux Work?
reduce method) that takes two parameters: An action, and a next state.
The reducer has access to the current (soon to be previous) state, applies the given action to that state, and returns the desired next state.
Reducers are designed to be pure functions; meaning, they produce no side effects. If you pass the same input values to a reducer 100 times, you will get the exact same output value 100 times. Nothing weird happens. They are completely predictable. As someone with a prominent "NO SURPRISES" sticky note on my monitor, this is a wonderful idea to contemplate.
Reducers do not store state, and they do NOT mutate state. They are passed state, and they return state. This is what reducers look like in action (did I mention that the dev tools are awesome?):
The above is the console after two actions: "Fetch Friend," followed by "Save Gifts." The first action passed
FETCH_FRIEND to the reducer with an updated friend object that is returned in the next state. The second action passed
SAVE_GIFTS to the reducer, along with an array of 4 gifts. The next state reflects this updated gift array. As usual, you can see exactly what existed in the state before and after this action occurred by simply inspecting each object.
Note that the reducer is passed only the slice of state that requires updating (the friend object and the gifts array, respectively), and it returns a new state after each action is dispatched.
How Do I Get Started?
Woohoo! You are entering a brave new world with me! Yay! It is very exciting, and also, at times, a real pain in the bottom. So before you dive in, some caveats:
- Redux and React are often used with a module bundler called Webpack. Webpack is incredibly powerful, and one day, it will be well-documented and easy to use (or it will wither and die of JSPM). In any case, that day is not today (Sept. 15, 2015). For now, do NOT worry about it, and just focus on learning the fundamentals of Redux and React with the resources below.
- Most code examples you see will use ES6 syntax. This can be confusing, and looks sort of... like Python and Coffeescript got drunk and had a baby. Fear not! Use Babel's transpiler tool. Just cut and paste the ES6 on the left, and you'll see familiar old ECMA5 on the right. Believe it or not, you'll get used to ES6 pretty fast (even the switch statements, I promise).
If you are looking for polished, well-documented code examples, and stable builds, then this is not the right time for you to try Redux. And that's totally fine. You can always come back in a few months, or when you have more time to dig through documentation.
I've gone through a horrifying quantity of material on flux, Redux, React, Webpack, ES6/7, and god knows what else. I think at one point I was reading about Drupal (don't ask). Below are the best resources I've found, optimizing for fundamentals, clear examples, and sound teaching strategies.
Required Basics (listed in order)
Live React -- 30-min talk by Dan Abramov, who basically invented Redux on accident (just like penicillin!). He explains Redux in beautiful amazing live code. It's especially useful to see, step-by-step, how he came upon reducers while paring down his flux workflow.
Thinking in React -- A fantastic guide to React by Facebook's Pete Hunt that helped me fully grasp how React and Redux are perfect for each other.
Redux Tutorial by François Bonnefont -- Of all the Redux tutorials and code examples I've reviewed (at least 20), this is easily the best at explaining the fundamentals, and walking you through each step. I recommend forking and cloning the repo, and replicating the code samples as you go along. Make sure to read the README. It includes important instructions on how to run the code examples from your terminal.
Advanced: After you complete the above, and are ready to build a full-stack app from scratch using Test-Driven Development practices, check out @Teropa's Full-Stack Redux Tutorial.
Immutable Data and React -- 30-min talk by Facebook's Lee Byron from React-Europe that explains why persistent, immutable data structures are the way of the future.
What the Flux, Let's Redux -- A good overview of why Redux is exciting. The author, Henrik Joreteg, also made a great Webpack template, if you need it later. But, as I said, don't worry about Webpack while you're still learning the basics of Redux.
Building CircleCI's Front-End with Om -- While not about Redux, specifically, Brandon Bloom does a really nice job of explaining the benefits and trade-offs of a similar implementation in the wild.
Official Redux Documentation -- Redux is still new, so keep in mind that documentation is still a WIP. Check out the examples when you're ready. Implement the Counter example to get a quick start. The most helpful example for me was the Real World one.
Awesome Redux -- A comprehensive resource list of everything Redux. I found this a little overwhelming to start with, but once you know what you're looking for, it's a great resource.
Official React Documentation -- The React docs are kind of hit or miss, but definitely worth reviewing and bookmarking. You will be referencing them a lot as you get to know React.