Rodrigo Rosenfeld Rosas

The missing bit in the React community: a common interface

Thu, 25 Jan 2018 17:52:00 +0000

I use Ruby for server-side programming, so I'll illustrate the issue in the Ruby community but it basically applies to all server-side languages. Even JavaScript I'd guess, although I haven't used JavaScript for server-side programming yet.

When it's time to deploy our Ruby web application, we're free to choose a web server from multiple options without requiring any changes to the application code most of the time. That's possible because all of them support the Rack specifications, which acts like an interface between Ruby apps and Rack web servers. When we choose a process-based server such as Unicorn, we can benefit from several advantages over thread-based ones, such as Puma, but the opposite is just as true, since a thread-based approach also has benefits over a process-based one. Other web servers are more suited to applications requiring long-live connections and would take yet another approach to connections handling.

The simple fact that we can easily switch the web server without changing our code to test the impact of different in our application is awesome, but Rack will also make it easier for new Ruby frameworks to be built and inter-operate with each other pretty easily. For example, you can mount Rodauth, which is a Roda authentication app, in a Rails or Sinatra app in a very straightforward way.

It's not really news that competition is awesome for consumers, and we, software developers, are consumers of libraries and frameworks, so we really enjoy competition of frameworks and libraries, right?

So, how could that be related to React in any way? After all, one might argue that React is competing with Angular, jQuery and others, right? Well, in that sense, competition still exists in the JavaScript framework/library market, but that's just not enough. I'll explain.

The Components problem

The way we build and use components are the biggest issue we currently experience in the JavaScript world. For example, the Material UI library doesn't compete with ng-bootstrap, for example. How could them? The components interface are completely different in React apps when compared to Angular apps. In that sense, we could consider React and Angular as two different languages. One wouldn't expect to be able to use the Ruby's Rack gem in the Go language, right?

So, yes, it leads to duplicate efforts to build great common components such as date pickers, sliders and so on. We have jQuery UI for jQuery, ng-bootstrap for Angular and Material UI for React (among many more options for sure). But it kind of makes sense to me that components implementation could be quite different because jQuery, Angular and React take completely different approaches. One might argue that we could concentrate the efforts on pure JavaScript solutions and build wrappers around it for each major library, such as React, Angular or jQuery. But I don't really think it would be that simple, so I'm not trying to suggest something like that.

When one creates a new programming language, it will take quite some time before it gets widely adopted. One of the reasons is that there's no ecosystem around that language initially. When Elixir was created, for example, there was no web framework available for it, of course. Ruby not only has a lot of choices for web frameworks but also offer several libraries for all sort of things you might need. Or Java. Or JavaScript, C++, whatever. Well stablished languages will take advantage over new ones exactly because of this existing libraries, making it hard for new languages to get traction. Unfortunately, I don't think there's something we could do to make it easier for new languages to be created and take advantage of existing libraries from other languages. But, fortunately, the React issue is much simpler to fix, if the community wants to.

What is different in React?

When React was born, it was really a game changer.

I'm not saying everyone should be leaving jQuery or Angular or whatever to jump to the React boat. There's no one-size-fits-all solution for creating applications. Angular, jQuery and React take completely different approaches when creating applications. Angular 1 has tried two-way data binding but gave up on it since version 2. Both Knockout.js and Vue.js still support two-way data binding. There are plenty of people that enjoy that feature. And for some use cases, it's certainly quicker to build some use cases with it when compared to newer Angular or React.

However, it's clear to me that React got way more traction than its competitors in the past few years. And there are plenty of reasons for that. I really think React did a great job in teaching us a new way to think about applications. It's not a secret that programming is mostly complicated because of state management, right? I still prefer OO programming over functional one, but I do agree with the most used argument from functional programming fans: managing state is hard. They claim functional programming is easier because it's easy to understand and test pure functions. It's the same sort of argument used by supporters of micro-services that will tell us that it's much easier to write and test micro apps than a big monolithic one.

And their arguments are not wrong. Just incomplete from my point of view. Because they hide the fact that they have moved the complexity to the integration part. By the way, it's perfectly possible to create modular monolithic applications whose parts can be tested and released independently while keeping the integration much simpler than with micro-services. I'm not saying one should never adopt the micro-services approach either. There's no silver bullet. The complexity will always exist somewhere and our job is to see what makes more sense for our project.

Anyway, I'll not get into this discussion in this article, but just want to highlight that the most complicated part when creating applications is state management. Truth be told, trying to keep your model and view in sync using jQuery has always been a nightmare, right? That's why we see several alternatives to jQuery for many reasons, like Knockout.js, Angular and many others. The main difference between them and jQuery is that they will let us manage the state outside of the DOM and will make sure that the DOM will change accordingly to the app's state. That's a big win over jQuery or any other DOM-based library.

So, why did React get much more traction than the alternatives? In my opinion, React is much simpler than the alternatives, while remaining flexible and fast. For example, Knockout.js, initial versions of Ember (I'm not following the current development, so I can't talk about recent versions) and Angular would all try to extend the HTML in sophisticated ways. They have to parse either the HTML template itself or some special tag properties and evaluate some special constructions. Knockout, for instance, would evaluate "data-bind" attributes, which resembles very closely a JavaScript object declaration. Both Ember and Angular would also offer their own control flow extensions instead of using plain JavaScript, because they preferred declarative (logic-less) templates. Maybe that description is not fully accurate as I never worked with Angular or Ember, but this is what I remember from the articles I've read back in those days.

React took a completely different approach, making the JavaScript developer life much simpler in several ways. At first, I (and many others) were scared by the JSX thing, when it seemed an heresy to embed HTML in the JavaScript, bringing memories from PHP and ASP to the mind. However, I (and many others) realized that it actually made sense. After all, I often ended up doing that myself in several cases using other methods. It might seem scaring at first, but quickly we get used to it and it makes sense. We'll even mix CSS and JS these days (also known as CSS in JS).

But JSX wasn't the main reason why people adopted React. I guess there are many people who actually adopted React despite the JSX thing, rather than because of it. There are two key ideas that set React apart and were responsible for its success in my opinion. And I'm not talking about documentation or component-driven programming because the alternatives also offered those.

One of them is the realization that keeping the model and view in sync is the major issue to be fixed by library authors. Approaches taken by Knockout, Angular and Ember seemed too complicated. Trying to figure out what has to be changed in the view when the application state changed was really tricky. Then, React decided to try a much simpler approach. What if we just rebuilt the entire app from scratch after any changes to the application? Well, of course the alternative frameworks could also implement that brilliant idea that simplifies a lot the implementation. Except that it would be painfully slow to do that.

So, that's the second idea, which allowed the first one to succeed, was the key for the revolution we've seen in the JavaScript scenario after React was born. The realization that JavaScript is pretty fast nowadays, as long as we can avoid the DOM, which is the slow part. And that's how the concept of virtual DOM became popular and today we have tons of alternative virtual DOM implementations. The realization that comparing an in-memory DOM was really fast allowed a not too complicated algorithm to update the DOM in a very efficient way to reflect the state of the virtual DOM. Of course, there are much more optimizations applied by React and similar alternatives, but the implementation of the virtual DOM diff algorithm allowed developers to quickly understand how to build React apps. It was much simpler to reason about than the alternatives, in my opinion.

And to make things even better, they adopted modern JavaScript, which allowed us to write Object Oriented programming in JavaScript without having to resort to CoffeeScript and alternative transpiled languages. When we add bundle builders as sophisticated as Webpack, things get quickly unbeatable. Now we're able to write modular conflict-less apps, using OO component-based programming, with an easy syntax to mix HTML in JavaScript. We can even even import CSS from JS or apply code splitting with Webpack. Or use some of the CSS in JS alternatives, such as JSS. But, again, the biggest gain is that we no longer need to touch the DOM directly, without having to learn a new template syntax. We just use plain JavaScript.

React is awesome, I know, so what is the issue after all?

The issue is that React is not just an implementation. It's a powerful idea and mind set. The concepts are so simple, that we now have many alternative to React which are mostly compatible with it. There are Dio.js, Inferno.js, Preact and NervJS to name some well known alternatives. Each of them could be competing with each other but unfortunately it's not that simple.

Why is that? Because you can't simply use some UI library designed to work with React in any of the alternatives. For one to be able to do that, they would have to use something like Webpack aliases, so that whenever the code imports 'react', 'react-dom' or 'create-react-class' they would be actually importing some compatibility layer around the alternatives. Something like 'inferno-compat'. What if we wanted to mix apps which are lazily loaded but developed by separate teams? Maybe one team is using React for a reason, while another team is using Inferno or Dio.js for another reason. Then the webpack rules get way more complicated to manage.

What if we could set up a common interface supported by all react-like implementations. Something like Ruby's Rack but for React components? UI libraries are basically React components. Basically they rely on JSX (not all of them, though) and React.Component. Both interfaces are very well known. JSX is already independent from React, but it still needs to know which pragma to use when parsing JSX. When using Babel, one can easily add a plugin that will include additional imports automatically so that you shouldn't be forced to "import React" in order to use JSX. But it would be great if we didn't have to resort to such things when targeting interoperability.

For React like programming I believe there's a fixed set of methods that should be enough for most apps. Functions such as like createElement (for JSX support, also known as "h" by some implementations), render, Component, createPortal and findDOMNode. What if we could create a meta package to provide us such method? Then all React-like alternatives could compete with each other by providing the implementation for such functions.

For example, let's suppose we create a new "react-like" package. We could set up which library to use like this:

1import ReactLike from 'react-like';
2import dio from 'dio.js';
3ReactLike.assign(dio); // or assign({createElement: dio.createElement, createPortal: dio.createPortal ...})

Then, instead of "import React from 'react'", component libraries, such as Material UI, could use it like:

1import React from 'react-like';
2
3export default Button extends React.Component {
4 render() {
5 return <button className="my-special-class">{ this.props.children }</button>;
6 }
7}

It would be even better if those libraries used yet another abstraction with a fallback to 'react-like', so that we would be able to use Inferno for some library and Dio.js for another one, for example.

Currently it's not an easy for us to pick up one of the great React alternatives out there because almost all component libraries seem to assume React is being used. Wouldn't it be awesome if we could provide a common interface to be used by components libraries and promote the competition among React alternatives?

Powered by Disqus