Hacker News new | ask | show | jobs
by dominic_cocch 3764 days ago
The backbone model api was made with totally mutable data in mind. React, on the other hand, only works well with immutable props and some mutable state in the top levels of components. That's our first issue, for sure.

Backbone also has a dependency on jQuery, which isn't very useful in React apps since mutatuing the DOM (one of jQuery's main purposes) is a no no. So most of the Backbone library isn't very useful to us anymore. I do still really enjoy the backbone way of fetching and saving models, especially in a Rails context. But I think we can get those features in other ways.

4 comments

That's pretty much experience too: our apps that use React + Backbone, I find myself more and more using a fraction of the features (very basic API integration (fetching, saving) and the router), both of which could be easily replaced with something else. Not that it would be particularly hard to extensively use Backbone with React, it's just that there seems to be other (better) ways to do the same things too.
I use React and Backbone too. Here's the source code: https://github.com/pixyj/feel/tree/master/client/app

I use jQuery for animations and also for non-crud pixel-level DOM manipulations as in the graph shown in the home page: https://conceptcoaster.com/course/python-tutorial/ In the graph, I need to dynamically calculate height of certain elements based on the height of others. The height cannot be predicted in advance. Can React work for such cases too?

You definitely can achieve it with React sans-jQuery, though it is definitely more boilerplate/code than the equivalent jQuery call. This is an acceptable trade-off for us, but might not be for you, YMMV
Really like the app. I like the top progress bar loading animation. I'm assuming this is the relevant file? https://github.com/pixyj/feel/blob/master/client/app/base/js...

Trying to figure out a way to implement this with React + React Router...

Thank you! Yes, that's right! I'm not familiar with React Router yet though :(
Taking Redux, for example; doesn't a Redux store's getState, dispatch and subscribe methods roughly map to a Backbone model's get, set and on methods respectively? Isn't the immutabability of data in a redux store an implementation detail of the store? How does this impact the React views?
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.

Personally, I'd say the biggest similarity between Redux and Backbone is that both are simple, minimal, "no-magic" implementations of a few specific concepts, and have communities that have built addons to fit additional use cases.

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.

Yes, exactly. I would argue that Redux has an even smaller scope than Backbone (e.g. it doesn’t handle views or AJAX) but the landscape has changed enough that this is not necessary for a state management library. But it definitely fills the same niche.
Thanks for the comprehensive reply. I think this answers my original question well. But in my follow up, I was basically asking if, from the perspective of the view, it makes much of a difference if it's communicating with a Backbone model or a Redux store.

P.S.I actually recently watched your Redux videos. Very nice!

>But in my follow up, I was basically asking if, from the perspective of the view, it makes much of a difference if it's communicating with a Backbone model or a Redux store.

I think it makes a difference. When you work with a Backbone model, you directly modify it:

    model.set('completed', true);
When the logic changes, you need to change the call sites to call a model method or several model methods.

On the other hand, with Redux the view specifies what it wants to happen and not how:

    dispatch({ type: 'TOGGLE_TODO', id })
When you need to change how the whole state tree responds to that change you don’t need to change the components. This is the difference.
Thanks for the insightful explanation. Adding https://github.com/atmin/freak to the comparison (disclaimer, I'm the author):

* it supports set operations, `model(‘fieldName’, newValue)`, like Backbone and unlike Redux, which trigger only the necessary update actions in the (implicitly built) dependency graph. set is idempotent, even if your computed properties have side effects

* it has .on() method

* "You can log every action and see the entire state tree before and after the action.”. In freak, too:

    const model = freak(statePOJO);
    const history = [];
    model.on(‘change’, prop => {
      // change is called exactly once per mutation
      history.push({...model.values});
    });
freak’s README is a literal test suite, consisting of state changes and assertions.

Probably it's a good idea to add 'beforeChange' event, to make it possible to cancel a mutation.

* it's currently implemented internally using mutable data structure, but this is implementation detail and can be changed to immutable data in the future, allowing simply `history.push(model.values)`

Since when does Backbone depend on jQuery?
> Backbone's only hard dependency is Underscore.js ( >= 1.7.0). For RESTful persistence and DOM manipulation with Backbone.View, include jQuery ( >= 1.11.0), and json2.js for older Internet Explorer support. (Mimics of the Underscore and jQuery APIs, such as Lo-Dash and Zepto, will also tend to work, with varying degrees of compatibility.)

It's not a true dependency but it's a default companion and optional dependency (see Backbone.$).

Source: http://backbonejs.org