8 ms·
From Backbone to React: Our Experience Scaling a Web Application
- misiti3780 12y agoThis 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 ?
- compedit 12y agoKhan Academy uses Backbone & React, checkout out their github. ex. https://github.com/Khan/perseus https://github.com/Khan/perseus React's blog usually has some open source projects featured as well - facebook.github.io/react/blog
- antihero 12y agoI'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.
- mpereira 12y ago"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.
- weavie 12y agoBecause 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.
- baddox 12y ago> 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. http://facebook.github.io/react/docs/pure-render-mixin.html http://facebook.github.io/react/docs/pure-render-mixin.html
- baddox 12y agoIn Om, I believe what you're looking for is cursors. https://github.com/swannodette/om/wiki/Cursors https://github.com/swannodette/om/wiki/Cursors
- asolove 12y agoYou'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 https://gist.github.com/evancz/2b2ba366cae1887fe621)
- prapam2 12y agoIf 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?
- dugmartin 12y agoThe 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.
- deleted 12y ago[deleted]
- jasonwatkinspdx 12y agoIn 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.
- antihero 12y agoOk, 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?
- dustingetz 12y agoyou 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/ https://github.com/dustingetz/react-cursor/blob/master/examples/helloworld/webapp/js/Page.js https://github.com/dustingetz/react-cursor/blob/master/examp...
- cnp 12y agoDo you plan and providing a write-up for this repo? I'd love to dive in but don't quite know where to start.
- dustingetz 12y agoi 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
- dustingetz 12y agoIt is written up now
- j_s 12y agoCortex is a library for React that is designed 'to support arbitrarily deep data structure[s] without requiring you to pass callbacks down the chain'. https://github.com/mquan/cortex https://github.com/mquan/cortex
- jasonwatkinspdx 12y agoYou might find this talk from one of the FB devs useful: https://www.youtube.com/watch?v=XhXC4SKOGfQ&t=7m https://www.youtube.com/watch?v=XhXC4SKOGfQ&t=7m
- ufo 12y agoHierarchical 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)
- capkutay 12y agoThat'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/ http://marionettejs.com/ 1: http://marionettejs.com/docs/marionette.application.html#the-application-channel http://marionettejs.com/docs/marionette.application.html#the...
- ben336 12y agoI'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 https://github.com/marionettejs/backbone.radio
- thejameskyle 12y agoBackbone.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/1796 https://github.com/marionettejs/backbone.marionette/issues/1...
- wuliwong 12y agoWe 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.
- swalsh 12y agoI 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.
- rakoo 12y agoYour 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 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.
- mikewhy 12y agoalso: using straight javascript for view logic versus some crippled template syntax is a huge win. for user in users UserComponent(user) is much better than some combination of `ng-repeat`, `ng-repeat-start`, `ng-repeat-end`
- crimsonalucard 12y agoAlthough 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.
- ufo 12y agoThe 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.
- jbeja 12y agoHad you check on this: https://github.com/Raynos/mercury https://github.com/Raynos/mercury it claims to be even more faster and maintainable than React.
- ufo 12y agomercury 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.
- outdooricon 12y agoVue.js has shown to be even more performant than React: http://vuejs.org/perf/ http://vuejs.org/perf/
- paultannenbaum 12y agoVue 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?
- jgj 12y agoI 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.
- lowboy 12y ago> 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.
- nchuhoai 12y agoI 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.
- jonkho 12y agoThis 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.
- cmaher 12y agoWhile 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.
- progx 12y agoThat 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.