Hacker News new | past | comments | ask | show | jobs | submit login
From Backbone to React: Our Experience Scaling a Web Application (techsonian.net)
96 points by akbarnama on Sept 17, 2014 | hide | past | web | favorite | 52 comments

I've still yet to figure out how to easily update nested datastructures with this immutable stuff.

Say we have a try of data, Rows -> Row{name, id}

Say we have a component, <Row row={row} />

On the Row, component, we want to update our "own" name, so with mutable stuff, (ignoring state updates), we could set this.props.name = "potato".

Say the structure of rows was managed by a statemanager, then how do we get that to do it?

Does rows do something like this.props.updateRowName(this.props.row.id, newName), then the statemanager "queries" the rows and somehow replaces the entire dataset tree with a new one where that specific row's name has been changed?

Really fascinated to know, as I'd love to move to this model.

Om seems lovely, but I'm not comfortable enough with Clojure(script) to move to it, let alone force it upon others in a team.

Hierarchical state and being able to update "my own" state is definitely where things get tricky in these virtual-dom.

As a rule of thumb, anything that wants to reference and mutate "its own" state should go in its own component and that state should live in the state, not in the props (props want to be immutable).

If something doesn't need to keep a hierarchical chunk of state then it doesnt need to be split up into a component class. Perhaps a `function render_row()` that returns virtual dom nodes is enough.

In your case, it might be simpler to keep all your state in the parent Table instead of splitting it into Rows.


> Does rows do something like this.props.updateRowName(this.props.row.id, newName), then the statemanager "queries" the rows and somehow replaces the entire dataset tree with a new one where that specific row's name has been changed?

At one point of the other, you are going to call setState() on some react component. In this example, this component could be the row itself the parent table or something higher up, depending on how you structured your app (there are pros and cons to each alternative here). Once you call setState() on that component, react will redraw the virtual DOM for that point down. Then, React detects where the virtual dom changed and emits real DOM mutations to update the screen.

In your question, if the parent table gets its state mutated, then it won't query the rows - it will just redraw all of them. This is not a big of a deal: redrawing virtual DOM is supposed to be cheap and you can use shouldComponentUpdate as a performance hint if you know that a certain subtree wont need to be updated.


Finally, there are design patterns that deal with mutating immutable data structures (lenses, cursors, etc) but its a bit of an orthogonal issue. When you use this the idea is that you have a single mutable state thing at the root instead of a hierarchical set of state components (or at least thats how I understand it goes - I dont know Om in depth)

you are right, this is a sticky spot when using immutability from javascript

- you can clone-and-mutate (I started here, but it quickly became a bottleneck in my app, and jr developers forget a lot leading to hard to find bugs)

- you can use real persistent datastructures (Mori), but now you have added a cross cutting concern - which type of array is the one i'm working with right now?

- you can use React.addons.update (I use this successfully, but you need to get used to a weird syntax)

Om provides an abstraction over React state called Cursors which provides immutable updates to react state at any point in a state subtree. I ported this concept to javascript (implemented in terms of React.addons.update) and have used this successfully in two large scale enterprise applications for regulated industries and I know of one other company using this. This provides a very clean and elegant programming model for immutable updates to react state.

https://github.com/dustingetz/react-cursor/ https://github.com/dustingetz/react-cursor/blob/master/examp...

Do you plan and providing a write-up for this repo? I'd love to dive in but don't quite know where to start.

It is written up now

i gave a livecoding talk that builds up to cursors at QCon NYC 2014 - it is not yet public but i can send you a private link if you email me

You've hit on the core idea, which is to make all data flow in one direction. Data passes down from the state manager to individual components, and then from those components back through the state manager to then go back and update the UI, etc. The hypothesis is that this is better than a style where you do pervasive two-way binding because you have a single point for managing all your data, validations, errors, undo/redo, etc. which are hard to manage if data changes occur at lower levels and have to be synced.

As for how you get the change request from your low-level component to that one place, there are a few options.

- Pass callbacks down to lower-level components in their props, as you show.

- Pass lower-level components a data structure that knows how to update itself within the larger data structure (a cursor, as used in Om)

- Allow components to issue change requests that get interpreted by the state manager. (See this article on how you can do typed change requests in Elm: https://gist.github.com/evancz/2b2ba366cae1887fe621)

If a user is editing a model then temporary changes would be stored in statemanger as well? If the user then decides to cancel the changes how would the app revert back to previous changes?

The fourth option is to use a global message bus which each component can post messages to. Data still flows down but the need for wiring callbacks is removed - each component in the tree just posts a message that is handled by whichever ancestor component owns the state mutated by that message.

In this model, the view doesn't "own" the name. The application state owns it.

Think of it as being similar to the classic one request at a time LAMP app. You query data from the database, then a template turns that into displayed content. When you want to change the data, you send an update transaction and then redo the query and the rendering to html.

It's just as simple here. The application state is like our database. We don't change it in part, down in some particular object, we write transaction functions that take the entire state to a new state. What's neat is our code is conceptually very simple: just functions that take take state as input and render a view. It might seem this would be absurdly slow, but the implementation doesn't have to be so naive. React does diffing under the hood to figure out the exact minimum changes needed to update the DOM after the state changes. Mori gives you similar optimizations when changing the state itself.

Ok, so I guess what I'm looking for here is how to implement our "database" as a client side data model that can do its thing, with the immutable structures (mori?) so that React can figure out what's updated super quickly, and a way to make sure the app knows that it's been updated.

I need examples, basically!

Ideally I'd have an API to my datastore, which in turn would handle telling React what I've updated.

It feels like I'm just not "getting" something with Mori.

Say our state looks like this in a normal JS structure:

    appState = {
        widgets: [{name: 'blah', data: {'text': 'hello', articles: [{}]}}, ...}],
        pageTitle: 'Hi!',
        user: {name: 'Derp'},
        notifications: 5
How would I even represent that with Mori?

Say I got a push from the server that I had 6 notifications, how would I change the state to represent this?

Say I wanted the widget to do an API call to update it's articles, and add any new article data to that. How would I do this?

Cortex is a library for React that is designed 'to support arbitrarily deep data structure[s] without requiring you to pass callbacks down the chain'.


Because it is immutable there are no updates made to the tree.

When you update a node, a whole new tree must be created with the new state.

It isn't as frightening as it sounds, the statemanager only needs to recreate from the root of the tree to the node that has changed. The rest of the structure can be shared between the new state and the old state as there are no changes.

It is a shallow but wide tree so we don't actually have to recreate that many pointers. In ClojureScript we have a maximum depth of 7.

The beauty of it all is because we have a new root every time one of the nodes is changed, if we need to check for a change we only have to compare a single node - the root. This makes for incredibly fast updates.

> The beauty of it all is because we have a new root every time one of the nodes is changed, if we need to check for a change we only have to compare a single node - the root. This makes for incredibly fast updates.

Hopefully (and presumably) this is true of any nodes nested in the tree as well, so that you can use the equivalent of the React PureRenderMixin to ensure that nested components only get re-rendered if one of their props has changed.


"Does rows do something like this.props.updateRowName(this.props.row.id, newName), then the statemanager "queries" the rows and somehow replaces the entire dataset tree with a new one where that specific row's name has been changed?"

Exactly. The state manager has "transactions" which are just functions passed down to components. These transactions are transformations on the entire application state, so to change the state you call them with data coming from user interactions and whatnot.

You might find this talk from one of the FB devs useful: https://www.youtube.com/watch?v=XhXC4SKOGfQ&t=7m

In Om, I believe what you're looking for is cursors.


That's interesting the author brought up marionette[0]. Marionette nudges developers to make all their views modular, reusable components. Also, instead of having a vanilla dispatcher object, they've added some more inter-app communication protocols[1]. I've had a decent experience so far. Has anyone else used marionette?

0: http://marionettejs.com/

1: http://marionettejs.com/docs/marionette.application.html#the...

We use Marionette in production at work. I enjoy it. I've supported existing Marionette applications and have also built a pretty complex calendar app using Marionette from scratch. Marionette seems to match up with my "intuition" as to how things should work. The ability to effortless swap modules and views in and out of regions is great. I'm not sure if that is hardwired into other Javascript MVC libraries and frameworks but if it isn't, I doubt I would like them.

I've had some other experience, I built a beta site for a startup using regular Backbone. I've also built a pretty complex site using Batman.js but it never got to see the light of day. Other than that, I've just toyed with Angular and Ember.

At work, we're moving from Backbone to Marionette. While it does make some really good choices from design/architecture standpoint (modules, messaging via radio), you still have to deal with the most painful part of building user interfaces - updating views on model updates. I'd recommend using it with a data-binding library if possible.

I spent the time to learn Marionette, and while it improved my Backbone code enormously, I found there was a pretty steep learning curve to using it correctly. And even so, I found doing certain things was often overly difficult or confusing.

Recently, I switched a small project from Backbone/Marionette to React and I haven't looked back. The learning curve for React is tiny compared to Backbone/Marionette, and it is so much easier to 1. reason about and 2. recompose. Not to mention much better performance!

It's clearly still maturing, but to me React feels like a big leap ahead of Backbone/Marionette.

I've really enjoyed Marionette as a more (but still not very) opinionated approach to Backbone development. You mentioned the messenger protocols, definitely checkout Backbone.Radio [1], it's being created to replace Wreqr, the current event implementation and has a much cleaner API. The plans seem to be to bundle it with Marionette 3.0 when it gets there, but its usable with the current version.

[1]: https://github.com/marionettejs/backbone.radio

Backbone.Radio is awesome (Bias Disclaimer: I helped write it with James Smith). It's definitely going to replace Wreqr in Marionette 3.0, and we'll be integrating with it much more.

There is also a lot of other cool things coming in 3.0. We're very focused on the architectural pieces and theres some really great ideas there. See: https://github.com/marionettejs/backbone.marionette/issues/1...

We use Marionette at work (and at my previous job as well), and I've got to say, I really like it. I've been a fan of Backbone for a long time, and Marionette just added a few more components to make it a lot more powerful. The app event system is easy to use, regions make view rendering and event delegation super easy, and the built-in support for collection views is great.

I also think Marionette is awesome and enforces modularity. The author mentions, "View A handling events from View B and vice-versa" which makes me think that he's may not be following the adage of "signal up/invoke down"; He may be just wiring up listeners in an adhoc manner and not establishing clear ancestor/child relationships with his views.

We've used marionette - wanted to like it but found the learning curve too difficult. Switched to React - much simpler learning curve with good performance.

Hi, Marionette core member here. I'd be interested in hearing about any trouble you've had while working with Marionette.

Side note: We're really rapidly re-writing our documentation right now (yay alliteration) so people can expect to see a lot of big improvements there.

Hey quick request!

You guys have one heavily-referenced example for your CompositeView. However, I believe some of the things in that tutorial are since deprecated (such as appendHtml):


The current documentation on composite view actually references that article.

Other than that, great work!

While I appreciate another look at React, I have to disagree with the article's conclusion that React is "objectively better" than other frameworks. The summary article of the story is basically "we completely re-architected our application and things are better now". There's nothing in Backbone that makes you construct the initial architecture on display, and you can use Backbone to build the final architecture as well. So, moral of the story: re-architecting an application you know more about than you did at the beginning is probably a good thing.

That is right.

But an architecture that is newer and learned from other architectures is probably a good thing too.

And React / Backbone / or whatever will not be the last architecture.

This article is interesting - I have been building large-scale backbone applications for a while and agree with a lot of this. Does anyone know of any good examples of open-source apps built with React ?

Khan Academy uses Backbone & React, checkout out their github.

ex. https://github.com/Khan/perseus

React's blog usually has some open source projects featured as well - facebook.github.io/react/blog

I ask this in like every React thread that comes up:

What is everyones experience with just using Backbone Models as your data source. We currently just pass the backbone models as props, but include a mixin that forces re-renders by listening to the model's change event.

I can see how at very large scales you'd want to move away from that, but for us it works great so far as we don't have to think that much about events, components are still just simply representations of the model in a functional way.

This is a legit. The concept comes down to if you know the diff of your models/collections, do you really need to do all the work that React does with diff-ing the virtual DOM to know what to render? If you know the diffs of the models/collections, and the logic of handling the diffs is bound via one-way binding through the mix-in then I do believe this is efficient and viable. I also would like others to discuss about this.

I never played with React, I like the idea though... however when I looked at the github I pretty much closed the tab after seeing the javascript directly rendering HTML.

Does anyone know if its possible to seperate that in such a way that Angular or knockout works?

Or am I just not "enlightened" about the way it works? Which is also certainly possible.

Your reaction is expected, because common experience among ui developers is that you should separate HTML from Javascript. This is supposedly called "separation of concerns".

I invite you to watch "Rethinking best practices", an introduction of what React is and why it was built so: https://facebook.github.io/react/docs/videos.html

You learn that separation of concerns is a good thing, but separating HTML and Javascript is not separation of concerns; it's just an arbitrary separation of technologies.

The mindset of React is that you build independent components, where each one contains everything to render information for the user. Whether you use 1, 2 or 3 technos (HTML+CSS+Javascript) for this component is not an issue and there's no reason to separate them.

also: using straight javascript for view logic versus some crippled template syntax is a huge win.

    for user in users
is much better than some combination of `ng-repeat`, `ng-repeat-start`, `ng-repeat-end`

I would disagree that this is an issue of separation of concerns, but instead it is a self-imposed separation for developer sanity. They are two different technologies, so one has to do a mental context-switch every time they go from writing View code to DOM/template code. Regardless if they are writing them in the same place or not.

I'd also much rather use a restricted templating engine like Handlebars so that my templates are forced to be "logic-less". I really don't think my template should have access to all of javascript.

Although I never used React, Angular has something similar to "components" called directives which you can configure into something similar to a component. The one issue I have with it is that if you want CSS compartmentalized into the directive it has to be inlined in styles attribute of your template string.

The phylosophy behind react is that you can use standard JS techniques to write your template logic. Since angular uses a fully separate template language then it needs to reinvent things like conditionals, loops, variable scoping, function definitions, function calls, etc.

Compared to other virtual-dom/js-as-a-templating-language tools, the most unique thing about react is the way they handle having a hierarchy of stateful components. Virtual dom is very simple if you only have state at the root level and rerender 100% of the page when something changes but it gets a trickyer if you want to distribute the state in a hierarchical manner.

I've used both and I found React's components much easier to understand than Angular's directives. If you want to create a reusable black-box directive in Angular, you have to understand all kinds of complicated stuff with scopes like isolate scopes and scope transclusion. To do the same in React is trivial because it just uses normal JavaScript functions and objects.

Maybe I'm misunderstanding; I'm not familiar with Angular. Surely with the right classes and ids you can put css anywhere that css can go? How could a framework limit that? Conversely, why would this sort of framework offer any other way to handle styles?

Its not that it's limited, there's just no feature added for styling. There's a place to add a template string in a directive but there's no place to add a "styling string". Typically you just put in classes and ids in your template string that references the global css file/s. If you try to reuse the directive in another project you have to move your css file over as well. The only way around this, or in essence to make a truly self contained and re-useable component is to inline the styles in the styles attribute. It gets a bit ugly in that case.

In my experience the jsx stuff works rather nice and looks like html where javascript just happens to be the templating language (apart from some gotchas listed here: http://facebook.github.io/react/docs/jsx-gotchas.html). And IMHO it pushes you to build small components which fit nicely together in the end.

But then, I like the full javascript approach from the mithril framework too and I haven't build something complex to date.

I agree it seems weird upon first looking at it, but after working with React for some time it feels absolutely sensical to do things this way as opposed to, say, moving your html templates into separate .hbs files.

You could take a look at [Ractive.js](http://www.ractivejs.org/). It's not quite caught up with other frameworks but it has a virtual DOM like React and mustache-style templating.

Had you check on this:


it claims to be even more faster and maintainable than React.

mercury is (intentionally) a much smaller and less featureful library than React. IIRC the biggest difference is that React components are stateful while Mercury prefers components to be fully stateless, with all the state living in the root.

Vue.js has shown to be even more performant than React: http://vuejs.org/perf/

> The results here are merely for technical reference and do not reflect the user experience in real products

That seems like it's of low value as a benchmark.

Vue is a framework that caught my eye a while back, and it's approach seemed like a nice compromise between backbone and angular. I haven't seen much published about it though, does anyone have experience working on a decent sized project with it, and if so how was the experience?

I made a file tagging system with node-webkit and Vue (+ gulp/browserify/partialify). Vue was a joy to use. Most of the complexity in the app was dealing with sqlite, Sequelize and many-to-many relationships. Once the data got into a Vue VM it was smooth sailing.

I did run into some performance problems (rendering huge lists into complex tables), but I was able to iron them out for the most part. The only downside, which is definitely not unique to Vue, was debugging--a ton of functions get called in the Vue code when model data changes, so it can be tricky to find the right combination of breakpoints to set to track down why what you're expecting to happen isn't happening.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact