| Unlike Backbone models, Redux state doesn’t have setters. Components can’t just go ahead and change it as they like. This adds more ceremony around changing the state but in my experience (I’m biased: I wrote Redux) makes such changes easier to debug and test. Every change comes as an action that the whole reducer tree can handle, so changes are predictable and reproducible if you record actions. You can change how actions are handled without changing the components dispatching them. A good example is how your components can emit SHOW_NOTIFICATION action but you can refactor your reducers from allowing only a single notification at the time to keeping a stack of notifications, without changing any of the components emitting them. This is what separating actions from state changes gives you. You can log every action and see the entire state tree before and after the action. This, in my experience, makes it easier to fix incorrect mutations spanning across multiple entities. If the user clicks a button and the state is updated, and then a network response comes, and the state is updated again, you can log diffs between the state changes and have a clear picture of everything that’s happened. In fact Redux DevTools even provide a UI for that, e.g. https://github.com/alexkuz/redux-devtools-inspector. One feedback I hear particularly often about Redux is that people who never wrote unit tests for front-end apps started writing them because it is just so easy to test reducers. You don’t need to mock any dependencies or simulate AJAX requests because any new data (whether local or remote) comes in the form of actions. So you can just call the reducer with a state and an action, and assert that its output matches what you expect. With a single state tree I found it easier to ensure that all state is normalized and you don’t have duplicate data. In Backbone, it is encouraged that models are instantiated as you parse the request which makes it non-trivial to normalize them and merge the changes. In Redux, any merging is going to be explicit, and you can always inspect the single state tree to make sure the structure is normalized and matches what you expect it to be. By contrast, in Backbone, models are active records so user.followUser(anotherUser) will set() a few flags on itself, fire off an async request, set() fields on another model, then possibly revert if the request failed. This is pretty hard to do consistently because there is no central point to mutations. You also have to build some sort of cache and entity resolve mechanism to make sure you don’t have duplicate out-of-sync models describing the same data. Finally, in Redux mutations are forbidden so it is easy for the views to bail out of reconciliation if the corresponding state fields have not changed. In my experience, Backbone doesn’t make such optimizations easy because it was designed with completely mutability in mind. So Redux, in a way, is similar to Backbone, but also is very different. |
Backbone is really just an event bus, some smart wrapper logic around updating an object and an array, and a nicer setup syntax for jQuery event handlers for a particular element tree. People have then built addons that handle relational models, derived data, view lifecycles, and so on.
Redux is just putting all/most your data in a single "model", making sure all writes happen explicitly and immutably, and firing a single "change"-like event. From there people have built various ways of organizing side effects, listening for specific changes, and hooking into other libraries.
So, both simple libraries that form the basis for an ecosystem, and let you pick-and-choose the pieces that you specifically need.