React: Another Level of Indirection
lispcast.com
lispcast.com
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
Of course, this varied by toolkit.
Otherwise, it seems like an interesting approach.
At Khan Academy we've decided to move to React for new JS development but we've had a lot of success moving to it incrementally and building new pieces in React without having to touch the old ones.
It also provides hooks that get called throughout the lifecycle of each component, including whenever it's been added to or about to be removed from the DOM. These can be used to integrate non-React Javascript behaviors that want direct access to the DOM, but you still have to properly handle cleaning up when React wants to take your DOM node away.
Ignoring animations and ajax, the core functionality of jQuery orients itself to the concept of progressive enhancement (http://en.wikipedia.org/wiki/Progressive_enhancement), you have a basic functionality of a page in HTML and you enhance that with scripts for better user experience. Hence most of jQuery methods follow the operations required for progressive enhancement: finding DOM stuff and changing it, registering events etc.
This is what in the end conflicts with the design of React. React is (again ignoring server-side-js prerendering) an all-or-nothing approach. The whole dynamic part of the DOM is constructed out of the virtual DOM. There is no no need to find DOM elements, because the React way would just relink your component to the real DOM result of its previous rendering or you find DOM elements of child components via React refs. To follow React design, you only change the real DOM directly if you need to step outside of React to e.g. integrate another library. The normal modus operandi is to just render new new virtual DOM based on your props / state.
You can do some meaningful things with combination of React and jQuery, e.g. just use jQuery's focusin/focusout to make up for the lack of bubbling focus support in React. Mostly though you shouldn't need to -- by design.
I believe the focus event bubbles just fine in React. If not, please file a bug.
At my previous workplace we were converting a classic ASP + ASP.NET Web Forms app to MVC. I really love MVC but I did like the component encapsulation in web forms.
https://www.quora.com/Whats-the-story-behind-Javelins-hilari...
I was talking about https://github.com/tailrecursion/javelin introduced in the talk http://www.infoq.com/presentations/ClojureScript-Javelin
https://github.com/couchbase/sync_gateway/tree/adminUI/utils...
(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.
[1] 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.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).
This has serious implications. For one, the browser inconsistencies are taken care of for you, both for DOM elements and for events (which are also virtual). You effectively get cross-browser DOM tags/attributes/events/etc. without ever knowing it.
https://github.com/facebook/react/tree/master/src/dom/compon...
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.
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.
I'm the real programmer.
And to the downvoters: My apologies for the offending you. I was simply trying to point out that the grandparent post is not comparing apples to apples, since it's very likely that he's achieving "fast" web development because he's either developing trivial user interfaces (i.e. not using anything that would require a layout manager, not required to support older browsers, etc.) or because he's using lots of other technologies in addition to the DOM (third-party javascript libraries, like jQuery, for example.)
The React todomvc examples also uses Backbone and/or Director.
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..)
http://www.quora.com/Pete-Hunt/Posts/Facebooks-React-vs-Angu...
Declarative syntaxes are easier to work with because you can look at your template and see exactly what the UI will look like. Since you're not doing the DOM manipulation yourself, it's completely airtight and you don't need to worry about writing custom DOM manipulation code for each individual part of the UI that can change. Of course, in any framework you can rerender the entire DOM by setting innerHTML on the top level but that tends to be slow and you need to watch out about resetting scroll position, input focus (and value), etc. React takes care of all of this for you.
(You're right that invalid user input is something you need to deal with regardless, but you should need to deal with that only when making forms, never when displaying data that you already have (which is the common case). As for fallback values, it's easy to write something like
<div>{this.props.author || "(anonymous)"}</div>
in React when building a view.)http://www.quora.com/Pete-Hunt/Posts/Facebooks-React-vs-Angu...
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.
So maybe it´s just a clojurescript thing, but i really feel like i'm missing something.
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.
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.
http://swannodette.github.io/2013/12/17/the-future-of-javasc...
The claim by David is that, "ClojureScript om based TodoMVC looks 30-40X faster than Backbone.js TodoMVC, which means other JS frameworks left completely in the dust".
https://twitter.com/swannodette/status/412033352699744256
There is some more discussion of Clojurescript and Om on Fredrik Dyrkell's blog on "A slice of React, Clojurescript and Om". The big idea with Om is to simplify idiomatic state management with Clojurescript’s immutable datastructures, while still getting all the performance. He has some nice comparisons with JavaScript and ClojureScript side by side.
http://www.lexicallyscoped.com/2013/12/25/slice-of-reactjs-a...
On Eric Normand's blog post "React: Another Level of Indirection" he talks about how there are four pieces of the puzzle in ClojureScript Web Development - and Om is the final missing piece: Problem: Global state management Solution: Atoms and persistent data structures
Problem: Client-server communication Solution: EDN (also solved pretty well by JSON)
Problem: Callback hell Solution: core.async
Problem: Stateful DOM Solution: React
http://www.lispcast.com/react-another-level-of-indirection
Form your own opinion of course, but these are some interesting claims.
All the major data binding frameworks can do deferred renders and are on the same order of magnitude. Most need to be configured for it but they can all do it.
I think React is a very clever solution to a problem I've been thinking about for years using an approach I would never have considered because I still forget how fast JS engines are. In terms of using it, I'm interested in React because of Clojurescript and not vice versa.
EDIT: Of course I find it just after I posted this! The link looks like a single link but it's actually two! :p
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), but if you're using Rails and Heroku, stay away from JSX for now
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.
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.
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.
What do you mean? Can you give me an 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), 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.
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.