Why Use Redux Over Facebook Flux

Why use Redux over Facebook Flux?

Redux author here!

Redux is not that different from Flux. Overall it has same architecture, but Redux is able to cut some complexity corners by using functional composition where Flux uses callback registration.

There is not a fundamental difference in Redux, but I find it makes certain abstractions easier, or at least possible to implement, that would be hard or impossible to implement in Flux.

Reducer Composition

Take, for example, pagination. My Flux + React Router example handles pagination, but the code for that is awful. One of the reasons it's awful is that Flux makes it unnatural to reuse functionality across stores. If two stores need to handle pagination in response to different actions, they either need to inherit from a common base store (bad! you're locking yourself into a particular design when you use inheritance), or call an externally defined function from within the event handler, which will need to somehow operate on the Flux store's private state. The whole thing is messy (although definitely in the realm of possible).

On the other hand, with Redux pagination is natural thanks to reducer composition. It's reducers all the way down, so you can write a reducer factory that generates pagination reducers and then use it in your reducer tree. The key to why it's so easy is because in Flux, stores are flat, but in Redux, reducers can be nested via functional composition, just like React components can be nested.

This pattern also enables wonderful features like no-user-code undo/redo. Can you imagine plugging Undo/Redo into a Flux app being two lines of code? Hardly. With Redux, it is—again, thanks to reducer composition pattern. I need to highlight there's nothing new about it—this is the pattern pioneered and described in detail in Elm Architecture which was itself influenced by Flux.

Server Rendering

People have been rendering on the server fine with Flux, but seeing that we have 20 Flux libraries each attempting to make server rendering “easier”, perhaps Flux has some rough edges on the server. The truth is Facebook doesn't do much server rendering, so they haven't been very concerned about it, and rely on the ecosystem to make it easier.

In traditional Flux, stores are singletons. This means it's hard to separate the data for different requests on the server. Not impossible, but hard. This is why most Flux libraries (as well as the new Flux Utils) now suggest you use classes instead of singletons, so you can instantiate stores per request.

There are still the following problems that you need to solve in Flux (either yourself or with the help of your favorite Flux library such as Flummox or Alt):

  • If stores are classes, how do I create and destroy them with dispatcher per request? When do I register stores?
  • How do I hydrate the data from the stores and later rehydrate it on the client? Do I need to implement special methods for this?

Admittedly Flux frameworks (not vanilla Flux) have solutions to these problems, but I find them overcomplicated. For example, Flummox asks you to implement serialize() and deserialize() in your stores. Alt solves this nicer by providing takeSnapshot() that automatically serializes your state in a JSON tree.

Redux just goes further: since there is just a single store (managed by many reducers), you don't need any special API to manage the (re)hydration. You don't need to “flush” or “hydrate” stores—there's just a single store, and you can read its current state, or create a new store with a new state. Each request gets a separate store instance. Read more about server rendering with Redux.

Again, this is a case of something possible both in Flux and Redux, but Flux libraries solve this problem by introducing a ton of API and conventions, and Redux doesn't even have to solve it because it doesn't have that problem in the first place thanks to conceptual simplicity.

Developer Experience

I didn't actually intend Redux to become a popular Flux library—I wrote it as I was working on my ReactEurope talk on hot reloading with time travel. I had one main objective: make it possible to change reducer code on the fly or even “change the past” by crossing out actions, and see the state being recalculated.

Sample Image

I haven't seen a single Flux library that is able to do this. React Hot Loader also doesn't let you do this—in fact it breaks if you edit Flux stores because it doesn't know what to do with them.

When Redux needs to reload the reducer code, it calls replaceReducer(), and the app runs with the new code. In Flux, data and functions are entangled in Flux stores, so you can't “just replace the functions”. Moreover, you'd have to somehow re-register the new versions with the Dispatcher—something Redux doesn't even have.

Ecosystem

Redux has a rich and fast-growing ecosystem. This is because it provides a few extension points such as middleware. It was designed with use cases such as logging, support for Promises, Observables, routing, immutability dev checks, persistence, etc, in mind. Not all of these will turn out to be useful, but it's nice to have access to a set of tools that can be easily combined to work together.

Simplicity

Redux preserves all the benefits of Flux (recording and replaying of actions, unidirectional data flow, dependent mutations) and adds new benefits (easy undo-redo, hot reloading) without introducing Dispatcher and store registration.

Keeping it simple is important because it keeps you sane while you implement higher-level abstractions.

Unlike most Flux libraries, Redux API surface is tiny. If you remove the developer warnings, comments, and sanity checks, it's 99 lines. There is no tricky async code to debug.

You can actually read it and understand all of Redux.


See also my answer on downsides of using Redux compared to Flux.

flux vs redux pros and cons highlights

The shortest, simplest answer is Flux has multiple stores and a central dispatcher to manage communication to those stores.

Redux only has one store and encourages using simple reducer functions to interact with various properties on the store.

This strategy makes it easy to reason about the overall state of your application (it's just one massive POJO) and allows for awesome devtools like time-travel debugging and hotswapping state.

There's a more detailed explanation (that does a far better job of explaining) in the official docs.

What could be the downsides of using Redux instead of Flux

Redux author here!

I'd like to say you're going to make the following compromises using it:

  • You'll need to learn to avoid mutations. Flux is unopinionated about mutating data, but Redux doesn't like mutations and many packages complementary to Redux assume you never mutate the state. You can enforce this with dev-only packages like redux-immutable-state-invariant, use Immutable.js, or trust yourself and your team to write non-mutative code, but it's something you need to be aware of, and this needs to be a conscious decision accepted by your team.

  • You're going to have to carefully pick your packages. While Flux explicitly doesn't try to solve “nearby” problems such as undo/redo, persistence, or forms, Redux has extension points such as middleware and store enhancers, and it has spawned a young but rich ecosystem. This means most packages are new ideas and haven't received the critical mass of usage yet. You might depend on something that will be clearly a bad idea a few months later on, but it's hard to tell just yet.

  • You won't have a nice Flow integration yet. Flux currently lets you do very impressive static type checks which Redux doesn't support yet. We'll get there, but it will take some time.

I think the first is the biggest hurdle for the beginners, the second can be a problem for over-enthusiastic early adopters, and the third is my personal pet peeve. Other than that, I don't think using Redux brings any particular downsides that Flux avoids, and some people say it even has some upsides compared to Flux.


See also my answer on upsides of using Redux.

When to use Flux (etc.) with React?

Redux basically gives you global state that any component can hook into.

So for your ajax call you'd have 3 actions. Check out the Redux docs for creating async actions.

GET_USER_PROFILE_START
GET_USER_PROFILE_SUCCESS
GET_USER_PROFILE_ERROR

Then you'll have reducers for

userProfile
isUserProfileLoading

When you fire your ajax request, your async action will first fire your GET_USER_PROFILE_START action, which will be picked up by your isUserProfileLoading reducer and will set isUserProfileLoading to true.

Then when the request comes back, if it succeeds, it'll fire GET_USER_PROFILE_SUCCESS with a payload of userProfile. Your userProfile reducer will receive userProfile and set it into state.

Your isUserProfileLoading reducer will also be listening for GET_USER_PROFILE_SUCCESS and will set isUserProfileLoading back to false when it sees that action.

If your request fails, you fire a GET_USER_PROFILE_ERROR action with an error payload and do some sort of notification in the UI. And your isUserProfileLoading reducer will also be listening for GET_USER_PROFILE_ERROR and will also set isUserProfileLoading back to false when it sees it.

Now any component in your app, no matter where it is on the page or in the component hierarchy, can look at isUserProfileLoading and render a loading spinner if it's set to true.

Hope that helps.

Why do we need Flux with React?

You don't NEED Flux the same you don't need MVC. They are both architectures and you can of course build something without using either.

Would you build a non-MVC app in 2016? Probably not, that doesn't mean that people didn't do it in the past.

Flux is awesome! But as most things in the tech industry is not always the right decision, things vary in a project requirement basis.

Probably the biggest selling point of Flux is that it tries to enforce the data flow in one direction, this means that you know for sure where the data is coming from. In a non-flux app the data for a component could be an own property, a property passed down the component tree, a local state variable, a state variable result of calling an API.

With Flux: "where does the data come from?". Answer: from the stores. Redux takes this further and only uses a single store.

Flux has been criticized because you need a lot of boilerplate code but again is a matter of tradeoffs.

At the end is always your call depending on your project needs.

Should flux stores, or actions (or both) touch external services?

I've seen the flux pattern implemented both ways, and after having done both myself (initially going with the former approach), I believe that stores should be dumb recipients of data from the actions, and that asynchronous processing of writes should live in the action creators. (Async reads can be handled differently.) In my experience, this has a few benefits, in order of importance:

  1. Your stores become completely synchronous. This makes your store logic much easier to follow and very easy to test—just instantiate a store with some given state, send it an action, and check to see if the state changed as expected. Furthermore, one of the core concepts in flux is to prevent cascading dispatches and to prevent multiple dispatches at once; this is very difficult to do when your stores do asynchronous processing.

  2. All action dispatches happen from the action creators. If you handle asynchronous operations in your stores and you want to keep your stores' action handlers synchronous (and you should in order to get the flux single-dispatch guarantees), your stores will need to fire additional SUCCESS and FAIL actions in response to asynchronous processing. Putting these dispatches in the action creators instead helps separate the jobs of the action creators and the stores; furthermore, you don't have to go digging through your store logic to figure out where actions are being dispatched from. A typical asynchronous action in this case might look something like this (change the syntax of the dispatch calls based on the flavor of flux you're using):

    someActionCreator: function(userId) {
    // Dispatch an action now so that stores that want
    // to optimistically update their state can do so.
    dispatch("SOME_ACTION", {userId: userId});

    // This example uses promises, but you can use Node-style
    // callbacks or whatever you want for error handling.
    SomeDataAccessLayer.doSomething(userId)
    .then(function(newData) {
    // Stores that optimistically updated may not do anything
    // with a "SUCCESS" action, but you might e.g. stop showing
    // a loading indicator, etc.
    dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
    }, function(error) {
    // Stores can roll back by watching for the error case.
    dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
    });
    }

    Logic that may otherwise be duplicated across various actions should be extracted into a separate module; in this example, that module would be SomeDataAccessLayer, which handles doing the actual Ajax request.

  3. You need less action creators. This is less of a big deal, but nice to have. As mentioned in #2, if your stores have synchronous action dispatch handling (and they should), you'll need to fire extra actions to handle the results of asynchronous operations. Doing the dispatches in the action creators means that a single action creator can dispatch all three action types by handling the result of the asynchronous data access itself.

What is the core difference of redux & reflux in using react based application?

I wanted to write another answer focusing on the specific difference between Reflux and Redux. @Mijamo goes into the core of why they originated as different things and is a very good summary if you have context, but I came to this question to know specifically the difference between the two from a development perspective. Seeing as how I just went in and read all the things, I wanted to write an answer. I will update this answer with more code examples.

Flux (Quick overview)

Before we go into this, I think one thing that we should keep in mind moving forward is thinking about current Flux and how it currently handles scaling an application with many components or many different pieces of states that needs to be managed. This is a pretty good talk at React NYC: Scaling Flux that goes into the problem and the solution they arrive at is not too far off from what Reflux and Redux allow you to do but in a nutshell a big question is "What do we do when we have components that have some shared state that they all need to keep in mind of? How do we handle and scale that?" Ultimately an answer a lot of these frameworks arrive at is we need this idea of a global state. Inevitably, both frameworks introduce some similar concepts to achieve this which we'll go into below.

Because I will need to reference a comparison of Flux, I just want to show a quick overview way Flux works with the picture below:

Sample Image

Reflux

In Reflux, there is no dispatcher, and the View Components directly communicate via the components through actions.

+---------+       +--------+       +-----------------+
¦ Actions ¦------>¦ Stores ¦------>¦ View Components ¦
+---------+ +--------+ +-----------------+
^ ¦
+--------------------------------------+

In terms of how it differentiates itself from Flux, there is not too much. You still create your own actions and create your own stores, and you still have your stores listen to actions. I believe the biggest difference is that in order to have the View components submit actions directly to the store rather than going through a dispatcher, the Components have a store property which comes from extending from Reflux.Component rather than React.Component so that it has a way to directly hook into a store. i.e. this example

class MyComponent extends Reflux.Component
{
constructor(props)
{
super(props);
this.state = {}; // our store will add its own state to the component's
this.store = StatusStore; // <- just assign the store class itself
}

render()
{
var flag = this.state.flag; // <- flag is mixed in from the StatusStore
return <div>User is {flag}</div>
}
}

You also have the ability to hook into multiple stores (there is a prop I believe called stores which takes an array and Reflux also ships with the ability edit mapStoreToState in case you wanted to control specifically how the stores pass over state to the components.

Naturally because you are using a component that Reflux ships with, you should probably read their documentation on Reflux Component and how to properly make components with this in mind

Last thing I'll note is above I mentioned the big problem was global state and how does this address that. Reflux does have a Reflux.GlobalState that can be contributed to as long as you set ids on your Stores. The link above goes a lot more detail into it but with this, you can access them via Reflux.GlobalState.[StoreID].[property] where StoreID is the id you assign the store and property is the actual piece of state you want to access.

Redux

Redux in and of itself changes a lot of things and also kills the idea of dispatchers. Before I go really deep into it, I want to highlight the three principles they mention in their docs.

  1. Single source of truth
  2. State is read-only
  3. Changes are made with pure functions

In Redux, there is really only one global state you have to deal with and that is the global state for your application (addressing the big problem). While you still have actions and stores, the stores themselves are simply responsible for keeping track of their own state in the global state tree, allowing you to dispatch actions to make changes to the state tree, and allowing you to access the state. You can also still put listeners on these stores via subscribe.

A large motivation of this goes into the first two principles. In Flux or even Reflux, if you wanted to make sure nothing was mutating the state when you didn't want it to (because technically you can access and change state in the stores whenever you want), you would depend on things like ImmutableJS to make sure you weren't accidentally mutating the state. Redux on the other hand makes it so you can only access the state via the stores/selectors and making changes only via dispatching actions (the third principle).

An interesting thing to note is that while Reflux and Flux had actions where in the stores you would listen and determine what state change to do, the stores in Redux simply dispatch a message with the payload you want and then that goes through a giant switch statement to determine what it should do with the state tree -- this is what they refer to as a reducer. This isn't any different from how Flux has reduce in its Stores but Redux rips this concept out as its own thing and your global state tree goes through a rootReducer (Redux ships with a nice function for you to combineReducers and make a rootReducer). A good way to think about it is you dispatch a change to the giant state tree and then whatever changes you want, it gets reduced or condensed to the final state you want. This actually influences how redux sets up a lot of things so it tells React how to rerender (assuming you are using Redux with React).

The data flow of Redux talked about really well in the link I mentioned above but there is also a pretty good infographic I have attached

Sample Image

So core differences is really

  • Redux has a completely different approach to state management -- it embraces the idea that there is a global state and that inevitably if you wanted to make changes, it should just happen there in a very specific way (how you handle what components have access to what state is up to you).
  • Reflux really tries to support giving components the ability to access multiple stores without having to change too much of what Flux was originally about (I would like to think Reflux is what Flux should have been).
  • Redux really changes how the state tree is managed and give the
    stores different responsibilities, and changes how state information
    is mapped down to the components, whereas Reflux simply rips out the
    middle man so you can have your components access whatever stores
    they need to more easily.

Hopefully this gives more insight to the core differences between them.



Related Topics



Leave a reply



Submit