Why use Redux over Facebook Flux?

I've read this answer, reducing boilerplate, looked at few GitHub examples and even tried redux a little bit (todo apps).

As I understand, official redux doc motivations provide pros comparing to traditional MVC architectures. BUT it doesn't provide an answer to the question:

Why you should use Redux over Facebook Flux?

Is that only a question of programming styles: functional vs non-functional? Or the question is in abilities/dev-tools that follow from redux approach? Maybe scaling? Or testing?

Am I right if I say that redux is a flux for people who comes from functional languages?

To answer this question you may compare complexity of implementation redux's motivation points on flux vs redux.

Here are motivation points from official redux doc motivations:

  • Handling optimistic updates (as I understand, it hardly depends on 5th point. Is it hard to implement it in fb flux?)
  • Rendering on the server (fb flux also can do this. Any benefits comparing to redux?)
  • Fetching data before performing route transitions (Why it can't be achieved in fb flux? What's the benefits?)
  • Hot reload (It's possible with React Hot Reload. Why do we need redux?)
  • Undo/Redo functionality
  • Any other points? Like persisting state...

  • 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 a function from the 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.

    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.


    In Quora, somebody says:

    First of all, it is totally possible to write apps with React without Flux.

    Also this visual diagram which I've created show a quick view of both, probably a quick answer for the people which don't want to read the whole explanation: Flux vs Redux

    But if you still interested knowing more, read on.

    I believe you should start with pure React, then learn Redux and Flux. After you will have some REAL experience with React, you will see whether Redux is helpful for you or not.

    Maybe you will feel that Redux is exactly for your app and maybe you will find out, that Redux is trying to solve a problem you are not really experiencing.

    If you start directly with Redux, you may end up with over-engineered code, code harder to maintain and with even more bugs and than without Redux.

    From Redux docs:

    Motivation
    As the requirements for JavaScript single-page applications have become increasingly complicated, our code must manage more state than ever before. This state can include server responses and cached data, as well as locally created data that has not yet been persisted to the server. UI state is also increasing in complexity, as we need to manage active routes, selected tabs, spinners, pagination controls, and so on.

    Managing this ever-changing state is hard. If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it's hard to reproduce bugs or add new features.

    As if this wasn't bad enough, consider the new requirements becoming common in front-end product development. As developers, we are expected to handle optimistic updates, server-side rendering, fetching data before performing route transitions, and so on. We find ourselves trying to manage a complexity that we have never had to deal with before, and we inevitably ask the question: Is it time to give up? The answer is No.

    This complexity is difficult to handle as we're mixing two concepts that are very hard for the human mind to reason about: mutation and asynchronicity. I call them Mentos and Coke. Both can be great when separated, but together they create a mess. Libraries like React attempt to solve this problem in the view layer by removing both asynchrony and direct DOM manipulation. However, managing the state of your data is left up to you. This is where Redux comes in.

    Following in the footsteps of Flux, CQRS, and Event Sourcing, Redux attempts to make state mutations predictable by imposing certain restrictions on how and when updates can happen. These restrictions are reflected in the three principles of Redux.

    Also from Redux docs:

    Core Concepts
    Redux itself is very simple.

    Imagine your app's state is described as a plain object. For example, the state of a todo app might look like this:

    {
      todos: [{
        text: 'Eat food',
        completed: true
      }, {
        text: 'Exercise',
        completed: false
      }],
      visibilityFilter: 'SHOW_COMPLETED'
    }
    

    This object is like a "model" except that there are no setters. This is so that different parts of the code can't change the state arbitrarily, causing hard-to-reproduce bugs.

    To change something in the state, you need to dispatch an action. An action is a plain JavaScript object (notice how we don't introduce any magic?) that describes what happened. Here are a few example actions:

    { type: 'ADD_TODO', text: 'Go to swimming pool' }
    { type: 'TOGGLE_TODO', index: 1 }
    { type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
    

    Enforcing that every change is described as an action lets us have a clear understanding of what's going on in the app. If something changed, we know why it changed. Actions are like breadcrumbs of what has happened. Finally, to tie state and actions together, we write a function called a reducer. Again, nothing magic about it — it's just a function that takes state and action as arguments, and returns the next state of the app. It would be hard to write such a function for a big app, so we write smaller functions managing parts of the state:

    function visibilityFilter(state = 'SHOW_ALL', action) {
      if (action.type === 'SET_VISIBILITY_FILTER') {
        return action.filter;
      } else {
        return state;
      }
    }
    
    function todos(state = [], action) {
      switch (action.type) {
      case 'ADD_TODO':
        return state.concat([{ text: action.text, completed: false }]);
      case 'TOGGLE_TODO':
        return state.map((todo, index) =>
          action.index === index ?
            { text: todo.text, completed: !todo.completed } :
            todo
       )
      default:
        return state;
      }
    }
    

    And we write another reducer that manages the complete state of our app by calling those two reducers for the corresponding state keys:

    function todoApp(state = {}, action) {
      return {
        todos: todos(state.todos, action),
        visibilityFilter: visibilityFilter(state.visibilityFilter, action)
      };
    }
    

    This is basically the whole idea of Redux. Note that we haven't used any Redux APIs. It comes with a few utilities to facilitate this pattern, but the main idea is that you describe how your state is updated over time in response to action objects, and 90% of the code you write is just plain JavaScript, with no use of Redux itself, its APIs, or any magic.


    You might be best starting with reading this post by Dan Abramov where he discusses various implementations of Flux and their trade-offs at the time he was writing redux: The Evolution of Flux Frameworks

    Secondly that motivations page you link to does not really discuss the motivations of Redux so much as the motivations behind Flux (and React). The Three Principles is more Redux specific though still does not deal with the implementation differences from the standard Flux architecture.

    Basically, Flux has multiple stores that compute state change in response to UI/API interactions with components and broadcast these changes as events that components can subscribe to. In Redux, there is only one store that every component subscribes to. IMO it feels at least like Redux further simplifies and unifies the flow of data by unifying (or reducing, as Redux would say) the flow of data back to the components - whereas Flux concentrates on unifying the other side of the data flow - view to model.

    链接地址: http://www.djcxy.com/p/51666.html

    上一篇: 为什么DOM元素为空?

    下一篇: 为什么在Facebook上使用Redux通量?