Reactive UIs with React and Bacon
joshbassett.info
joshbassett.info
What can you do easier using react.js (and backbone) that you can't already do using knockout and plain html or whatever markup you prefer?
To me, there is a reason to abstract javascript and similar logic outside the plain html -- however the workflow of including script sources inside html that in turn include html that gets rendered is backwards.
The biggest complexity in frontend development is that there is too much state. Most people, when doing frontend dev blame JavaScript for the difficulty. JavaScript is a problem, but it is not the problem. The problem is the management of state.
Compare this to server side development. It is much more robust. Why is that? Because there is too little state, what you render has a little lifetime. Request comes, response generated, there you go.
What's the problem with frontend? The app there has a much higher life. You get the page, interact with this, many interactions later, you accumulate state, but you are still dealing with the same "response". Hence the difficulty of building UI's.
So, what's the solution to this? One: you need to manage your state properly. Two: through this state, you need to be able to define your UI at any point in time.
Assume you solved these problems, but naively, that is, once state changes, you re-render the whole response. This would be much less bug prone. But now you have a performance problem: Changing state will not necessarily change all the UI response. Hence the genius of React's virtual DOM, which acts an intermediary, diffs the different responses over time, and redraw only what's necessary to redraw, not the whole UI response.
tl;dr it's about expressiveness
1) In a virtual DOM system your view code is a black box, while in an observable-based system you need a way to specify all the data bindings. This means that virtual DOM systems can use usual Javascript abstractions (functions, varuiables, etc) where observable systems often need custom DSLs (for templating, data-binding, etc). Additionally, data binding can be tricky to get right when it gets more complicated, for example, if you have a sorted list of items it needs a way to know that should be rerendered when the inner properties change.
2) Performance: in a observable-based system, the memory and CPU costs are related to the size of the model while in a virtual DOM system the costs are related to the size of the view. This can be a performance gain for the virtual DOM if you have a big model but use windowing to display only part of it. Also, the O(n) cost on the size of the view is usually not a problem since views tend to be small enough that that fits inside one repaint frame.
Also, JSX is a relatively lightweight wrapper that uses native JS constructs for looping and subroutines. This is in contrast to things like Angular where the templating system has its own way of scoping variables, abstracting out subtemplates, looping over arrays, doing conditionals, etc. Angular must use these special DSLs and abstraction mechanisms because there is no way to do full data binding with just plain old Javascript code.
The Reactor pattern came more out of the back-end world where suppose you have a data stream that you have to filter and do more processing to create more data stream (e.g., person A,B post cat gifs on Wall on client -> pushed to respective the Facebook Wall stream for person A, B -> piped to Facebook Feed for person C who is subscribed to [A,B] -> combined with Facebook real-time ad-stream for demographics group X that person C belongs to -> pushes the ad-infested social media crap down to the user C on client),
Now normally all of these would be traditionally done via a HTTP RESTful Ajax pull request from the client-side and the back-end service would query the database or some kind of caching service (Redis/Memcache) and perform some application logic to combine the data and send it down to the client-side.
Reactor pattern tries to do away with the service layer and instead have the JS clients as first class citizen subscribers in a publish-subscribe model and pushes the streams of data down to the client.
So you can think of some really exciting use-cases, like Pornhub/Youtube/Spotify processing streams of everyone who is watching a video at any given moment; then showing everyone the chain of top "branching-off" of video's that everyone is watching and have the people who have this shared "experience" chat and lead the group to the next recommended video and so forth for a much more collaborative and real-time media consumption/"discovery" experience.
This is something that you might not notice (and it might not even be relevant) if you just need to drizzle a little Knockout throughout your app/site for a little interactivity, but the lack of abstraction really starts to hurt once you go beyond simple interaction.
EDIT: Oh, I should also mention the Virtual DOM, but that's not really that relevant unless you're experiencing performance problems. It might also be very handy for server-side rendering, but I don't have actual experience with that, so YMMV.
In React it's just simple JS which you write exactly where you need it, namely where you instantiate your component -- it doesn't even have to escape the scope (vs. KO where it "pollutes" the view model in the "owning" component so to speak).
EDIT: A little addendum: It's been a while since I've used KO in any serious way, but another issue is that of embedding arbitrary components inside another. One thing is generating DOM from whole cloth, but if I have a component which just takes other components as input then the KO model breaks down.
Yes, coming from a traditional JavaScript MVC background it's hard to let go of this idea, when it's been touted everywhere for years. But the fact is, there's no fundamental reason why we need to put these technologies into separate containers.
I recommend this talk by Pete Hunt: https://www.youtube.com/watch?v=x7cQ3mrcKaY
edit. oops, linked the wrong talk
True separation would be if there will be the HTML file and then there will be a separate definition for the data bindings using selectors, because then you don't have to touch the HTML when you change bindings.
React cannot do that but the other useful feature i was looking for is that it can render the views on the server-side (node.js) too.
but they've provided JSX on top which means you can essentially write "plain html" as with any other application.
i really prefer having my template and it's logic in the same language. one option is React without JSX, another is clojurescript.
the short answer to your question is that Backbone and Knockout (afaik) don't do diffs to update your DOM.
What is that something? This is indeed a recurrent problem with all these new tools and technologies: small examples that don't reveal the issues you will have when your system starts to get larger and more complex.
Our design/css/markup person loves it, because with JSX she can trivially write familiar HTML, and at the same time she's highly encouraged to make little reusable building blocks (components, in React lingo).
The result is that our view codebase is remarkably well structured, even though very little of it has been touched by seasoned hardcore programmer types. React pulls us into this "pit of success" of locality, reusability and clarity.
I'll braindump what we do, for those interested, just to share an alternative approach as the one listed in this blog. I'm not sure whether it's better or worse - we're probably doing some things in ways that could be better, so I'm very open to questions and critique, too.
Our frontend architecture is Model-View-Nothing. Our view is pure React components, with a root component called View which has, as its state, the current page and the model. We also have a thin "proxy" with a function for each backend API call, but it has no state so it's not very interesting in terms of architecture.
The model is a simple hierarchy of classes. We use TypeScript for these classes, but it could've been CoffeeScript, ES6 or JS-hacked classes as well, no difference. Our root model class owns all other model objects, hierarchically.
A user action typically causes our model to want to extend or update itself. It does API requests to our backend for this. In the case of PUT/POST/DELETE, we update the local model first, and then asynchronously send it to the server - our data is such that we can assume that most of the time, the operation will succeed. This is great, because we can re-render the view before the server even acknowledges the operation's success. This makes the app is very snappy.
We even do the same with GET requests: very often, we already have some of the data we're getting (e.g. if you go to a user profile, you typically clicked a link with that user's name and avatar on it). When it makes sense, we just show the page immediately with what data we have, and show a little loading animation for the rest. Again, this makes the application feel very responsive.
This works for us without any additional tools, because our model layer somewhat resembles the structure of the view. I suspect that this is quite common (e.g. if you're displaying a list of tweets by a user, you probably have a "root -> user -> list-of-tweets" structure both in the model and the view). It allows us to pass only the model data to a React component that it needs, making our React components very slim and reusable.
In effect, our model layer is one big client-side cache. The methods on the model deal with synchronizing this cache, and the view just re-renders whenever the model tells it to. Another benefit is that if users navigate to pages they've seen recently, we still have all the data, and can show it right away (and refresh it in the background).
All of this works great on mobile too. Especially when viewing a page you saw before (e.g. by hitting "back"), the difference is enormous: many mobile browsers cache very little history (at least my Chrome doesn't do it much), but for our page, it's instantly there.
We would've gone for this dynamic no matter what, because we want to make a web app that feels like a native app. Nevertheless, I believe that React allowed us to do it with amazing simplicity and effectiveness. Other tools, such as Spine[0] encourage the same asynchronous dynamic, but I believe that without React, we would've spent a lot more time fighting with state, asynch timing issues, and all that.
How do you handle failed PUT/POST/DELETEs and cases where the user is offline? This has been our biggest hurdle; mainly because when users feel like what they're using is a desktop app they think less about internet connectivity and assume the app should work offline (oy).
Currently I have the model save revisions of its data (one large document) through pouchdb but any solution with offline capabilities at some point needs to figure out conflict resolutions. I haven't got too far into conflicts/diffs but I'm curious if that's something you guys dealt with.
So, in short, you're ahead of us here.
Not an Angular lover, but devs use Angular because it's the quickest way to build LOB clients with javascript. It's no legacy or whatever,just pragmatism. People coming from flex/wpf know what I mean.
Angular doesnt answer all problems and is not for all use cases but for LOB clients it handles 90% of use cases and problems. Same cannot be said for React without 3rd party libraries.
No framework can ever be a silverbullet, I would not use Angular for an app with a lot of transitional states and animations.
In theory you could use React with Angular but frankly I dont see the point.
> but only for the same reason as why people still use PHP: legacy
? You just cant compare apples and oranges, a javascript framework and a serverside scripting language.
Anyway, in my experience angular is half the code, half the time, and half the cost.
http://www.google.com/trends/explore#q=Angularjs
Well will you look at that, angular seems to never get less popular.
The idea of react as a better compositor for angular keeps iterating back around in my head, which makes me wonder if a sort of 'final answer' will turn out to be somewhere between the two - a $watch isn't that much different to a reactive stream if you squint, after all.
This can be done in angular too, but I don't understand why you would want to give someone the assumption that an operation was successful before you know if it actually was.
Currently, Director is configured inside in the root view component, and at every URL change, we do a setState with new page information and relevant parameters. Then, in render() have a big switch over all accepted page names and render the appropriate SomethingPage component with the appropriate props (usually the root model object and some parameters from the URL).
I feel like there must be a more elegant way with less repetition (if you go to /#profile/moo, we do setState({page: "profile", ...}), and in render we check if this.state.page equals "profile", to instantiate a ProfilePage - that's 4 times the word "profile" if you include the URL, a bit too often for my taste). But it's just boilerplate and it's local to one file so we can live with it for now.
Open to ideas though!
As an aside, React runs great on Node; we don't touch the DOM or browser APIs anywhere at all. One more plan that we have is to run our entire frontend on the backend too, once we drop the "#". The idea is to just have it call the "real" backend (the API) like it would always do, to render the very first page for a URL. Great for SEO, too, and no need for hashbangs or duplicate view-rendering code.
I'd be very interested if anyone has any experience using React to render initial page layouts like this.
I'm actually working on an app architecture (within a toy project) that does just this; it uses react-router-component for the routing (so routes work both on both ends without environment specific stuff) and the api is built with racer (https://github.com/codeparty/racer) so that data access on the client or server is unified too
Repo: https://github.com/gnijor/Reaxer
Toy app in question: http://reaxer.nodejitsu.com/ (also contains a keyframed animation written as a react component for fun)
That doesn't actually seem that bad, if you consider that half of that repetition is so that you have distributable URLs that can be opened up on another browser thousands of miles away with no other state information.
You could just have an anchor tag with no src attribute, but with onclick=setState(...). That removes the repetition, but you don't get a shareable URL.
I don't completely understand your question about state trickling down, however, so I'll just blarb another braindump.
Let's assume a page where we render a product review. A Review react component gets, as a prop, an instance of a Review model object. If a user writes a comment on a review, then a Review react component fires an onNewComment event (also a prop), which we tie to an addComment method on the Review model. This model stores the change, sends a POST to the backend asynhronously, and returns a trigger to the view that something changed. The view does a full page-wide rerender (which is fast because React).
We keep all "data" kind of state in our model, all addressible via a core Model object (of which only a single instance will ever exist). The entire tree of React components is built up with props, no this.state at all, except on the root React component (that we call View).
One exception to that rule is state that is only there to help the user, and not "real" data. For example, whether or not some panel is collapsed, or whether or not a few items are selected for a group operation, fancy effects, etc. The kind of things that we're OK with forgetting if the user would navigate away from the current screen. That stuff we do as locally as possible in a component with setState.
I'm not sure to what extent all this is a best practice though. I talked to the React guys on IRC (they're very approachable and very kind!), and they say that a global refresh and preferring props over state is what they do too, so that's something at least :-)
Did this answer your question?
In other words: I haven't really seen a need for a library like Bacon when using React, because React provides a means for generating a view without "wiring it up" as you do in a typical reactive app. You just regenerate the view whenever an action is taken that might have changed the model.
Even then, if one were to have a single object that store the entire state of the application, this object could easily be saved to localStorage or pushed to the network for syncing to other clients. If the user were to reload the application or perhaps resume use on another device, they would be presented with exactly what they left behind. The infrastructure and code to do so would be very trivial.
The days of state being spread over and managed by a number of objects has come to an end, at least for me.
There's so much overlap yet I can't find a detailed comparison or discussion of the two directions. The mailing list discussions I've found the devs seem very dismissive of each others projects.
https://gist.github.com/Kequc/11083050
Then your class:
class MyApp.Cat
constructor: (data) ->
@element = $('<div class="cat"></div>')
@update = new MyApp.Update @,
name: (v) =>
@element.text v
color: (v) =>
@element.css backgroundColor: v
@update.all data
new MyApp.Cat
name: "Gary"
color: "#FF00FF"If you've built large-ish things manually or with Backbone, as I have, this kind of thing is about 80% of your code. Just doing bookkeeping and manually futzing around to keep the dom in sync with your data.
Now compare with React, Angular, or Ember. Your list view just says: take all my todos, order them, and display each one in this way. It's the framework's job to deal with creating and destroying, reordering, and updating those views in order to keep the DOM in sync. This ends up cutting out lots of code while producing better results, since the framework is written by smart people and thoroughly tested, whereas ad-hoc list view implementations are generally either buggy or have memory leaks.
MyApp.CatManager =
init: ->
@container = $('#cats')
@collection = {}
add: (cat) ->
@collection[cat.data.id] = cat
@container.append cat.element
remove: (cat) ->
delete @collection[cat.data.id]
cat.element.remove()
all: ->
$.map @collection, (cat) -> cat
get: (id) ->
@collection[id]
arrange: ->
sorted = @all().sort (oa, ob) ->
a = ob.data.name
b = oa.data.name
if (a < b) then -1 else if (a > b) then 1 else 0
$.each sorted, (i, cat) =>
@container.append cat.element
true
Allows you to maintain full control over configuration and of what is going on. I have a question, Angular was built by very smart people at Google that is undeniable. But, does Google use Angular in their products?I think that if the application needs to be sufficiently complex and the complexity maps to exactly what an existing framework does then you should use the framework that it fits.
Generally often does that happen?
Unless you have experts writing these things by hand, using a framework is the quickest way to minimize performance problems and memory leaks.
Now what if the user can change the cat's name by clicking on it and editing the div content,but cant give a cat a name already taken, because this cat is part of a collection of cats on that web page, which have the same editing features as Gary? Of course, cats are sorted and any change to one cat updates the sort order.
Suddenly your 39 line framework becomes worthless.
gary.update.all jsonResult
Even if you aren't storing state server-side. In the update attr methods have name changing check the rest of your cats for an existing name.
What's the advantages to writing half your app in js and half in coffee script?
at least it isn't half coffeescript half typescript this time.
FRP only starts to excel when you need to weave multiple signal sources together. Examples includes key combination for certain hot key. Or signal switching when implementing drag and drop.
I am considering using React but am stuck supporting IE7(!). Can anyone share their practical experience using React with clunky browsers?