
React: Another Level of Indirection - mercer
http://www.lispcast.com/react-another-level-of-indirection
======
clarkevans
I've been playing with Facebook's React to make an internal interactive
resource scheduling system for our company. So far I think I'm getting it...
and am thrilled with how it works.

React eschews the attempt to split the View / ModelView that are the hallmark
of most full-service frameworks. Instead of this separation, React
applications are built by defining reusable, nested components, each having a
render() method that mixes binding and logic to build sub-parts of a web page
using their custom DOM. Each component has an internal mutable state and a set
of read-only properties it gets from its parent context. In React, "data
binding" is just part of rendering.

Since React owns the DOM, it's incompatible with JQuery; or rather, if you use
JQuery, you could do it in a leaf node and manage the boundary carefully.
React tracks things by using a hierarchical identifier, each key unique within
each level of the structure. It then has a predictable/fast algorithem for
detecting changes and updating the DOM. Unless you provide your own component
keys (used to build the DOM id) all the way down, you need to use CSS classes
(rather than IDs).

Finally, I'd say, React really seems like a library -- and one that has a low
API to functionality ratio. That is, it really does something substantial with
relatively few things you need to learn. If you have your own in-house
framework, it might be something you could weave in gradually rather than do a
wholesale adoption of a larger (measured via API surface) framework.

~~~
Pacabel
I haven't used React, but the approach you describe sounds very much like how
early C++ UI toolkits worked. Applications would be built of "reusable, nested
components", often called "widgets". The widgets would be placed into a
hierarchy, with widgets often creating their ancestors. Widgets would have a
paint() method that would take the widget's internal state and the state of
the widget's container(s) into account when displaying (or updating a previous
display) of the widget.

\- Pacabel

~~~
wavefunction
One thing I'm not clear from your description is... The widgets were tightly-
coupled with their ancestors? (In order to create them)

~~~
ori_b
In general, no. You would typically take your widget, create it, and put it
into a widget-agnostic container or layout manager. The widget wouldn't know
about it's parent or children. The widgets would communicate by sending
signals which bubbled up or down the container heirarchy, triggering repaints,
resizes, and so on as needed.

Of course, this varied by toolkit.

------
jchrisa
I spent the holidays building a four screen admin UI with lots of fast moving
tables and lists, and React was able to work with me from who-cares spaghetti
all the way to clean enough to hand-off to a team, all without missing a beat
or imposing much up-front cost. I'm a fan now.

~~~
porker
As someone who spends his entire life writing admin screens and more user-
friendly but complicated to develop variants of CRUD, I don't suppose you
could release the source code of what you wrote?

~~~
jchrisa
Always do. I plan to debase this before merging to master (the history is a
mess) but here it is

[https://github.com/couchbase/sync_gateway/tree/adminUI/utils...](https://github.com/couchbase/sync_gateway/tree/adminUI/utils/assets/jsx)

(Edit to link to my jsx views.) Also I only barely used the forms stuff so I
haven't got an opinion on it yet.

~~~
porker
Thanks. I've yet to find forms I love in anything, but not tried Reactive's
take yet :)

------
jules
Am I the only one who doesn't like React? It is an ad hoc hacky solution to
the view update problem. Diffing DOM trees is not just done for efficiency,
but also for correctness. If you have a textbox somewhere with the cursor in
it, and you update some other part of the DOM because of a network event, you
want to retain that cursor position. If you just blindly put a whole new DOM
on the page then the user would be confused because the focus on the textbox
and the cursor position would be lost. With DOM diffing you have to keep in
mind how exactly the diffing is performed because it will have actual visible
effects on the user interface when user interface elements have state that is
not represented in the model. You just have to hope that Reacts diffing gives
you the behavior that you want. And what about interface elements that have
internal state but which are implemented in Javascript rather than in the
browser itself? React doesn't do anything to solve the view update problem for
them. Seems to me that we need a more principled solution that tackles the
problem head on, instead of a hack that happens to do the right thing most of
the time.

~~~
spicyj
React's diff algorithm is completely deterministic. For form inputs and other
components which have their own state, it's recommended to put a `key`
attribute on each one so that React understands what you meant if you reorder
elements in a list: [http://facebook.github.io/react/docs/multiple-
components.htm...](http://facebook.github.io/react/docs/multiple-
components.html#dynamic-children). Idiomatic React code usually has very
little state except at the top of the tree so this is rarely a problem.

~~~
jules
Right, but why do diffing in the first place? First generating a whole new
tree and then computing the diff is (1) wasteful (2) leads to problems because
you need to find the correspondence between nodes to preserve state. If you do
something like Self-Adjusting Computation [1], you can still write your
program as if you are generating the DOM as a function of your data, but the
machinery under the hood can compute the diffs _directly_ by keeping track of
the dependency graph and the correspondence between the old and the new is
fully automatic.

[1] [http://www.umut-acar.org/self-adjusting-computation](http://www.umut-
acar.org/self-adjusting-computation)

Assume you have a function f that takes the model data as input and computes
the DOM as output:

    
    
        dom = f(model)
    

If we now do an update to some model data, that has some effect on the DOM.
When executing f for the first time, the machinery of self-adjusting
computation records the dependencies of the different dom elements on data in
the model. If some part of the model is updated it can propagate the changes
along those dependencies to compute the parts of the dom that change as a
result.

~~~
dgreensp
David from Meteor here. We're moving away from DOM diffing for essentially the
reasons you describe. If you assume that views are written in a template
language, which is the norm, then diffing is usually unnecessary. I'd never
heard of self-adjusting computation, but it sounds a lot like what Meteor
does! Thanks for the link.

All that said, I think React's model is great, and performance is actually
fine. The React guys are not naive about browser performance; they've thought
way harder about it (and tested more) than the commenters here seem to
realize. The diffing is performed in pure JavaScript and is very fast.
Creating a framework for declarative views in JavaScript is a pretty tough
problem, and diffing is a major tool in the toolkit of ways to make it
possible, the same way treating certain objects as immutable or storing JSON
in your database are tools that enable certain styles of programming.

What pulled Meteor away from diffing as a central paradigm was the desire to
stay closer to today's web development techniques and make things simpler for
the developer. We wanted to provide the ultimate automagical version of
templates and jQuery rather than something new you have to understand. When
you have templates, you don't have to lean on diffing as much. When you have
jQuery in play, you make fewer assumptions about owning the DOM (in React,
every DOM element is backed by a component).

In the long term, the jQuery part will fall away as declarative templates and
components do the heavy lifting and the DOM is seen as less hostile (with IE8
retiring, for example).

------
daemonk
I wonder if it's possible for browsers to start implementing this natively.

Have the browser make a shallow copy of the DOM and allow developers to
manipulate the shallow copy. Then have a update() function that will take care
finding the diffs and update the actual DOM. Seems like it would be much
faster than a javascript implementation also.

~~~
girvo
At that pint you have now implemented a desktop UI styled API 'n the browser!
I'm not against that by any means, I just think it's funny :)

~~~
jt2190
This is _exactly_ the goal. The DOM's strength was representing documents, not
user interfaces. In this era of web apps, trying to use the DOM to build UIs
is a massive pain. The web is really years behind in providing a native UI
tool kit, and all of this DOM abstraction is just an attempt to paper over the
real problem: The DOM is not good for building UIs.

~~~
crazygringo
> _The DOM is not good for building UIs._

Funny, after years of Win32 C++, VB, and then web development, I find I can
slap together an interface so much faster and better-looking using HTML/CSS/JS
than anything for the desktop.

However, "slap together" may be the important phrase -- putting together a
well-architected, consistent, extendable, etc. interface is perhaps a little
hairier. But the DOM is _great_ for building UI's quickly, with trivially easy
custom styling and graceful resizing.

~~~
jt2190
You just build UI's using the DOM? No abstraction libraries like jQuery?
You've just won my "real programmer" award.

~~~
recuter
Technically jQuery normalized the DOM between browsers and added some stuff
like QuerySelectorAll that is now standard so you don't really necessary need
it anymore for anything.

------
chenglou
React user here. Some comments seem to suggest that React doesn't play well
with other libraries; it's really the opposite. Here's a React Chosen
component in 25 lines of code: [https://github.com/chenglou/react-
chosen/blob/master/react-c...](https://github.com/chenglou/react-
chosen/blob/master/react-chosen.js)

The React todomvc examples also uses Backbone and/or Director.

------
bsaul
I've tried to understand what´s so special with react from the project's home
examples, but it seems to me like an api similar to angularjs's directives,
only using javascript to build the widget´s html instead of a template. Could
anyone here with an experience of the two enlighten me?

Edit : to be more precise, i've written fairly complex directives in angular
and i don't understand the problem react is better solving. Synchronisation
between dom and model is something you have to deal with manually anyway since
it often includes validations rules and default behaviors, that can't be
guessed easily ( invalid user input, no value appearance, etc..)

~~~
grayrest
If you're already using directives to get all your app's state out of the DOM
then there's not a lot of practical benefit to using React over Angular. You
get some perf gains due to the DOM update batching and it's conceptually
cleaner. I think Angular is better for a mixed designer/developer team if the
dev builds directives with the goal of letting the designer work independently
since non-directive Angular is extremely easy to learn.

The excitement here is that React is a MUCH better match for Clojurescript.
The React model is a one way rendering into a virtual DOM, computing a minimal
edit list against the real DOM, and applying it in one batch. The idempotent
render lines up with pure functions. The batch nature lines up with the
epochal time model of Clojure's vars. Clojure's persistent data structures
allow react to bypass model mutation checks.

I discussed the topic of client side state with various people at a couple NYC
Clojure meetups in the spring and at Clojure/West and everybody had ideas for
a solution but nobody had THE solution. The React model, if not React itself,
is that solution for Clojurescript. I'm disappointed that I didn't look into
the implementation details of React when I checked it out this Summer and
found it unremarkable.

~~~
bsaul
Not sure i got this right, but my client side state lies in the model
javascript layer (i'm talking single page application here), and the synchro
between model and DOM is handled both by two-ways data binding (not exclusive
to angular) and directives for micro DOM states. I don't quite see the need
for diffing at the DOM layer, if you can already observe the changes on the
model layer and recreate the Part of the DOM...

So maybe it´s just a clojurescript thing, but i really feel like i'm missing
something.

~~~
grayrest
You pretty much have it. React is a better match with the Clojure programming
model.

Pulling your application state completely out of the DOM is the key win and
everything else is incremental gains/taste.

Since I care about this a lot I'll write more stuff:

Consider the basic conceptual model of Backbone. Your model changes, you get
an event, you run your model object and matching template through your
template engine, you get an HTML string and innerHTML it into the right place
in the DOM. It's a simple functional transform of data into DOM in response to
an event and the simplicity is a large part of the initial appeal of Backbone.

The issue with the event->model+template->innerHTML sequence is what I call
the idempotent update problem. The transform from model into the DOM is
idempotent and as long as your model and template are the same the resulting
DOM subtree will be identical. This is a problem because identical means
blowing away all the state that was previously in the DOM subtree: event
handlers, form fields, subcomponents, widgets from other libraries, etc. In
small apps, this isn't a problem you just event delegate the listeners, be
careful around your forms and widgets and you're fine. The problem is that
when your app gets big enough you want to split it into components so you stay
sane and once you do that you invariably want to nest your components and the
idempotent update problem bites you hard. There are workarounds and people
manage but you lose the conceptual simplicity of the Backbone model almost
immediately.

The benefit of the virtual DOM/diffing approach is that it lets you retain the
simplicity of the idempotent transform of model to DOM while solving the
idempotent update problem. Of course, with React you have the issue of getting
things back from the DOM to your model that two way bindings give you but two
way bindings are incompatible with the Clojure model.

Angular templating system and bindings deliver the same benefits and it
remains my preferred javascript solution to the problem. Other people complain
about the introduction of a lot of extra things you need to know (scopes,
change propagation, directive phases, digest cycle), how things can interact
strangely (directives and repeats), and that the system is monolithic
(providers, DI, promises). These are legitimate complaints but I've been
interested in this problem for years and I have yet to find a system that
doesn't have legitimate complaints and angular is roughly equal in complexity
to anything I've found that covers the same ground.

~~~
Estragon
Could you give a more concrete example of the idempotent update problem,
please? It's too abstract for me to follow.

~~~
grayrest
Here's a fiddle:

[http://jsfiddle.net/H4ADp/](http://jsfiddle.net/H4ADp/)

The counter shows the portion of the application state you're tracking while
the contents of the textarea show the part you're not. It's obviously stupid
but the contents of the textbox are an easily visible way of allowing you to
mutate the DOM (other mutations could be attached event listeners,
instantiated jQuery UI widgets, etc) and see it undone.

I'll add that even if it seems like you can track all the state (e.g. add a
textarea field to the model and set a keyup or blur to save the contents for
re-render) that's a disaster. If you're typing in the middle of the textarea
the cursor position isn't remembered. You can store that too but what if you
resize the text field? The list of edge cases is endless. You really want to
bypass it by using something that updates the DOM intelligently.

~~~
Estragon
Thanks, that example makes the issue very clear.

------
craigching
In David Nolen's blog post on Om, he has a link to an Om version of TodoMVC.
Does anyone see it at TodoMVC? I can't seem to find it.

EDIT: Of course I find it just after I posted this! The link looks like a
single link but it's actually two! :p

------
feifan
I got started with React on a project last week. It's magic when it works, but
initially it takes some thinking to figure out how all the components should
fit together, and where the interactions should take place. It took me a while
to figure out that my interactions on one component might trigger a method
that should belong on a parent component a few levels up, and to have to pass
that method down in the props for the child to call.

React provides a syntax called JSX that looks a lot like regular HTML, but is
transformed into native JS before it hits the React engine. It's brilliant,
but requires node somewhere along the line to do the transformation. I got
stung by this — building a Rails app, and Rails on Heroku ships with an
ancient version of Node that causes the transformer to error out. Solution in
the works ([https://github.com/heroku/heroku-buildpack-
ruby/pull/177](https://github.com/heroku/heroku-buildpack-ruby/pull/177)), but
if you're using Rails and Heroku, stay away from JSX for now

~~~
acjohnson55
In a Backbone project I'm working on, every app component and view passes a
context object down to any children it creates, which is a copy of the context
object it received upon creation, optionally augmented with methods it
contributes from itself and other providers. So it's basically a component
tree, with an API that grows as you descend down the branches (yet remains
distinct between branches). In essence, the context forms a sort of scope that
subcomponents can use to communicate with others.

By convention, components "import" API calls into themselves in their
constructors. This makes it very clear what the dependencies are.

The beauty of this setup is that intermediary objects don't have to worry
about explicitly mediating the interface between their ancestors and
descendants. The only components that have to do any mediation are common
ancestors of components that need to communicate across branches, but this
mediation can be contained, rather than polluting the entire hierarchy.

It's also trivial to mock out the context methods during testing. Similarly,
you can use a sort of name-based polymorphism to provide alternate
implementations of the context methods, dependent on configuration or parent
component, perhaps.

It seems to me that a similar scheme would fit nicely with React's model.

------
ttty
After 1-2 hour of playing I've converted my jquery view (still not 100% same,
but almost) to a react, look here. There is a mini tutorial too.
[https://gist.github.com/totty90/8209324](https://gist.github.com/totty90/8209324)

~~~
filipncs
That definitely belongs in an external gist or something similar. I'm
surprised hacker news doesn't have a max length.

~~~
ttty
fixed, sorry

------
ChristianMarks
My takeaway:

 _Any other problems left? I can 't think of any. That's something to discuss
on Twitter._

Meaning that the remaining problems are trivial misconceptions that can be
disposed of within 140 characters.

------
xixixao
For a quick try of React in CoffeeScript, I put together
[https://github.com/xixixao/mimosa-hyper](https://github.com/xixixao/mimosa-
hyper)

------
joesb
It would be great if someone can extract the virtual DOM engine part into its
own library. New framework/library with different approach in other area can
be develop while utilizing the virtual DOM part.

At least I would love to see the combination of Angular directive (ability to
combine cross-cutting aspect into a single DOM node and, may be, digest loop)
with React's virtual DOM.

------
jgalt212
ReclineJS is pretty awesome, albeit a bit slow, with larger data sets. I'd
wonder how snappy it would be if it were ported over to react.

[http://okfnlabs.org/recline/](http://okfnlabs.org/recline/)

------
Edmond
Like this a lot! especially the live update example on the website. I think we
are beginning to see how web developer tools that are browser-based make
certain things possible that would never be possible with native tools.

~~~
dkersten
_how web developer tools that are browser-based make certain things possible
that would never be possible with native tools_

What do you mean? Can you give me an example?

~~~
Edmond
Yes there are many...for instance visual layout driven web development is one
obvious example.

I know people who prefer native tools always get defensive about the idea of
the browser becoming the default platform for web development (and possibly
software development in general), but I really don't see it happening any
other way.

The ways browser tools for instance can use the browser itself to help
simplify web development is something native tools are never going to be able
to do. For instance I envision being able to integrate the debugging/developer
facilities built into the browser directly into my browser-based developer
product without much heavy lifting.

I may be bias given that I am a founder of a startup (see profile for link)
building a browser-based developer product...but I have also spent years
working with native IDEs including attempting to build my own IDE
([http://bit.ly/IwZCEL](http://bit.ly/IwZCEL)), the browser advantage is just
apparent to me. Granted there are still improvements to be made to browsers in
order to bring them to parity with native environments for software
development in general but I think it is just a matter of time.

~~~
dkersten
Oh ok, I think I understand what you mean.

Though working with QML in QtCreator has been a pretty streamlined and
effective experience for me and for web development, at least _right now_ (ie
like you say, this will improve in time, in which case you may well be right)
I see browser tools (eg the debugger) as a part of a much larger toolchain
(editor, REPL, etc).

Though you can build a native editor that has, for example, webkit embedded to
augment what the tool can do. Or do something like LightTable which is, AFAIK,
build on top of an embedded browser. I think a hybrid approach gives the best
of both worlds. Then again, with the trend of everything moving into the
browser, I guess this shouldn't be necessary in the future.

