Hacker News new | past | comments | ask | show | jobs | submit login
Reactive UIs with React and Bacon (joshbassett.info)
75 points by nullobject on Apr 19, 2014 | hide | past | web | favorite | 55 comments

Maybe I am not getting it, but to me react.js seems like a bad way of wrapping your view html in javascript.

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 big idea is the render function in React describes your UI at any point in time. Not initially only, at any point in time.

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.

Knockout templates also represent your UI at any point in time. The only significant difference as far as I can tell is that React feels strongly that pure JavaScript (with an optional preprocessor that lets you type HTML in your JS) is a better way to organize UI code than templates with bindings. With Knockout custom bindings, this difference becomes much smaller.

I had to cut the Knockout-specific slides, but I do cover this topic in depth here: https://www.youtube.com/watch?v=h3KksH8gfcQ

tl;dr it's about expressiveness

I found the presentation really interesting.If I had to give a slightly larger TLDR the two main points I got from it were

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.

How does JSX not count as templates with data bindings?

In that context, data binding means that you observe the model and set up a data flow so that you only need to update the specific parts of the view when the model changes. JSX doesn't do that as all it does is render the view. You need to detect and trigger repaints separately and when that happens it will blindly redraw everything instead of just updating the relevant DOM nodes.

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.

JSX is just syntax for instantiating data structures. Nothing more.

Very interesting talk. Thank you for sharing

I think you're pulling more concerns about integration with existing JS MVC frameworks (e.g., Backbone/Ember/Angular) than just reacting.

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.

Knockout is pretty good, but the big deal about React vs. Knockout is components and the reusability you (can) get from componentizing your view code/html.

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.

I think angularjs is better than knockout if u r looking for full blown mvc app in Js as it supports DI and testability. On the downside, angularjs have lot of configuration

Angular is a framework, knockout and react are data binding libraries. You can get dependency injection and testing with both, its just something you have to set up yourself.

It seems to me that Knockout's custom bindings provide the same reusability as React. The only difference I see is that React recommends their JSX preprocessor to allow HTML right in the JS, whereas Knockout custom bindings require building elements using the JS DOM API or hooking up some preprocessor manually.

Custom bindings which generate DOM get you some of the way, but not quite the whole way. One of the disadvantages with custom bindings is that they are rather tightly bound to how the view model looks. If your binding expects something which isn't directly in your view model (at the right level) and perhaps requires a little transformation of your view model, you sometimes end up adding "synthetic" computed observables just to transform from your real view model to the view model that the custom binding expects. This is not really encapsulated very well. (I hope that makes sense, it's kind of hard for me to explain.)

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.

I think I'm in the opposite situation. I've used Knockout recently, but haven't used React (I've only read their documentation). I can't think of an example of what you're talking about when you say that custom bindings are bound to how the view model looks, and that sometimes "synthetic" computed observances are needed.

> 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

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

Knockout too pollutes the HTML with it's own "language" in the "data-bind" attribute (for example: data-bind='value: itemToAdd, valueUpdate: "afterkeydown"')

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.

Sure, though I'd argue that knockut's data-bind attribute makes sense to fit inside HTML as it makes any sort of debugging a lot more straightforward (with the exception of handling context and what hierarchy your specific model is in at this very moment).

the reason your html is modeled at the javascript level is because of the DOM efficiencies it provides.

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.

That's not particularly relevant because Knockout doesn't need to do diffs--it uses the observable pattern, so in most cases it knows what changes have been made the moment they are made.

"Perhaps this example is a little too contrived, but the real power of reactive streams becomes apparent when you want to do something more complex."

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.

My team has been using React full time since half January. What felt like a gamble initially, turned into mad love. I understand why people still use e.g. Angular, but only for the same reason as why people still use PHP: legacy. React is simply that much better. I hope its ideas will spawn many followers.

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.

[0] http://spinejs.com/

This is a similar approach to what we're doing, except I went with Ractive for the view layer instead of React.

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.

We don't really deal with it at all yet. Good warning! That said, given the nature of our app, I think that for us, displaying an error bar on top and caching the last PUT/POST/DELETE in a slow retry loop is probably enough - it's a content-heavy app, so people will understand that the content comes from the internet. I hope. :-)

So, in short, you're ahead of us here.

> I understand why people still use e.g. Angular, > but only for the same reason as why people still use PHP: legacy.

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.

Angularjs first release was about 6 years ago so it's hardly legacy. It's almost as if the OP was trying to plug React..

Anyway, in my experience angular is half the code, half the time, and half the cost.


Well will you look at that, angular seems to never get less popular.

> In theory you could use React with Angular but frankly I dont see the point.

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.

>we can re-render the view before the server even acknowledges the operation's success

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.

because many operations will be a success 99.9% of the time and it's often possible (and better overall UX) to handle the error case gracefully a second or two later.

Yep, 'optimistic updates' is the term I like. Alex Maccaw wrote a good article on the subject a few years back:


How do you handle URL routing (what happens when I click "view tweets" on a user page) and generation (how do you generate the src attribute on the "view tweets" `a` tag)? Are you using anything special for handling URL history and push state?

We're using Director, like daGrevis in the other comment. Currently, our URLs look like http://devlocal.izooble.com/#profile/someone but adopting the History API (where available) for dropping the # is part of the plans.

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'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)

> I feel like there must be a more elegant way with less repetition

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'm doing something similar[0] like author is doing and for URL routing I use Director[1].

[0] https://github.com/daGrevis/Hendrix

[1] https://github.com/flatiron/director

Interesting. Can you tell a little bit more about your 'hierarchy of models'? How does state trickle down to child-models, etc?

It's a pretty traditional data-heavy class diagram, it looks pretty much like the kind of class modelling they teach you in college. Our site is about product reviews, so we have users and reviews and products and stuff like that as aggregate roots. Reviews can have comments, users can have profiles, etc etc. A big composition tree, really. We avoid storing the same data twice (and thus, sometimes outdated) by unique'ing the aggregate root objects by their id.

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?

I think it did answer the question. Very nice overview!

splendid! Going to cook something up. Cheers

The use of setState in React strikes me as a code smell. React has two ways to provide state for components: props and state. I prefer using React in a more functional style: your model is managed separate from the view and updates are handled just by re-rendering the view from the top (which React handles efficiently).

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.

I agree. Using setState within a nested component should be limited to UI-only functionality, such as tab views or toggling menus or the like.

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.

Am I the only one frustrated by the seeming lack of communication and mindshare between devs working on React and those working on Web Components (polymer.js).

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.

All of this complex front end framework stuff I really don't see that it is necessary. You can write basically the most important part of the framework into a very small coffeescript file.


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"

Ok now let's try to write a Todo List app. We'll have a TodoListView that shows a set of TodoViews. The TodoListView will need to initialize instances of TodoView for each item in the list, and react to model change events to know when to create or destroy new child views, so somewhere we'll need a map from model id to TodoItem view to do the book-keeping and cleanup. The child views will be displayed in order based on one of the model attributes, so whenever that attribute changes on a model, we'll need to find the relevant views and manually reorder them in the dom. And what happens when something in the todo item changes: do we re-render the whole thing (don't double-attach event listeners!) or selectively change individual parts of the dom?

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.

Build a javascript object.

  MyApp.CatManager =

    init: ->

      @container = $('#cats')
      @collection = {}

    add: (cat) ->

      @collection[cat.data.id] = cat
      @container.append cat.element

    remove: (cat) ->

      delete @collection[cat.data.id]

    all: ->

      $.map @collection, (cat) -> cat

    get: (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
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?

This implementation will be incredibly slow for large lists. Every time any one of the elements moves, you will make N dom changes rather than the needed one or two. In an individual case, you can hand-write the faster version. But when you have multiple components like this interacting, knowing how to do a minimal change is not an easy task.

Unless you have experts writing these things by hand, using a framework is the quickest way to minimize performance problems and memory leaks.

Writing an observer class in easy, Managing the state of hundreds of them is not.

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.

I am presuming that the application stores its state server side, if that is the case than this is not at all useless. Presumably you are sending the edited name to the server the server sends back the result of the edit. If the result of the edit was that the name was unavailable and therefore remains unchanged then this won't do anything:

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.

Are you required to mix coffeescript and plain javascript like that?

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.

A lot of this is over kill. FRP has a lot of complexity overhead before things get easier. For task like simple data binding as shown by author, your regular data binding library (angular, knockout) is enough. Doing the same with Bacon only makes it more complex.

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 love how Reactjs is getting more and more popular, i was yearning for something of quality to reach the top in the js world this days, and React seems like a very good competitor. And what i like most of it, is as simple as jQuery.

There is also a todomvc with undo using baconjs. It's pretty neat when you can use Bacon model:


Thanks to so many sharing their experience here.

I am considering using React but am stuck supporting IE7(!). Can anyone share their practical experience using React with clunky browsers?

React is ie8+. I think it could be made to work on 7 fairly easily, we just didn't need it for FB so we never tested it.

Applications are open for YC Summer 2019

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