Monday, June 22, 2015

A web developer's unrest #2

This is a small series of blog posts where I try to find out how to best build UI components for the web. I build on Dojo, but for its upcoming 2.0 release, a decision is yet to be made on the successor of its widget system, Dijit. In this series I compare Dijit with some newer techniques now available for creating UI components, with the ultimate goal to be able to decide upon a long-term strategy myself.

Part 2: State


State is a bit of a neglected fire hazard in UI tools. The danger is that there are so many changes going on in your components that you tend to loose track. This is especially true when you have a single-page application. Dijit doesn't force you to create single-page applications, but when you run many components it makes sense to work that way, especially once you start handling navigation. That may seem like a bad idea, but it does give you full control of the program, its data flow and persistence, DOM rendering, transitions/animations, even styling, but comes with the ultimate price: you, the developer, have to manage state. That is as far removed from traditional web pages as one can get, because the WWW is initially stateless: client state is not retained by the browser.

Back in the day, Javascript libraries used to extend the DOM. Dojo chose instead for decoupling, where isolated pieces of code render and control a portion of the DOM. As a consequence, Dijit is entirely stateful: each instance of a Dijit inherits first from the Stateful class, which ensures it has a "get", "set" and "watch" method, and so its own internal state. Updates of the instance's properties may be reflected in its DOM attachment. Although most changes are shielded from the API, it does mean that when you want to create a custom component, you have to master Dojo's way of managing state. It is perhaps mostly due to this aspect that Dojo is said to have a steep learning curve.

A good reason to introduce state is for example the support of WAI-ARIA for custom components, to make them accessible to people with disabilities. In other areas it's less obvious. For instance, Dijit's layout is managed through a diversity of containers and content panes that can be nested to create desktop-like screens. Any resizing of a container sets off a chain of calculations, each retained in the widgets concerned for optimization. This was introduced to enable a layout system in older browsers, but can and should be avoided nowadays.

How does Polymer deal with state? It is handled firstly by two-way data binding: properties are stored in plain old objects, referred to as the "model". The DOM is hijacked: Javascript is fooled into interacting with the model as if that were the DOM itself. So, when the model changes, the DOM changes, but also vice versa (hence two-way data binding), entirely in sync. What I just called "hijacking" is an alternative way to avoid extending the native DOM, and more sophisticated than keeping pointers. This means there's a bit less state involved, but this doesn't mean that Polymer is entirely stateless: the state is in the model, and managing it can become as opaque as it can become with Dijit.

Finally, while looking at how React.js handles state, the first thing that struck me was that there's actually a property explicitly called "state"! There are also methods that seem to pertain exclusively to handling state. Apparently the React.js developers are coming more from a functional programming paradigm, as in functional languages state is something you have to achieve, rather than it being at the basis, like in javascript. However, at the top of the developer's guide it says: Components are Just State Machines. So much for introducing state where it ought to be, and not throughout the component library, right? Wrong! React explicitly tells us to not use state everywhere, but only where needed:
Most of your components should simply take some data from prop[ertie]s and render it. However, sometimes you need to respond to user input, a server request or the passage of time. For this you use state. Try to keep as many of your components as possible stateless. By doing this you'll isolate the state to its most logical place and minimize redundancy, making it easier to reason about your application.

With the above quote I end the second part of my attempt to find my bearings in UI development in the year 2015.

No comments:

Post a Comment