Tom Dale
3 min readSep 2, 2016

--

That would be nice, but in my experience, framework-agnostic components are a long way off. It’s one of those ideas that sounds obvious in theory, but doesn’t work out very well when the rubber meets the road.

Why? It all boils down to data flow. The way that data flows between components differs greatly between frameworks. How you think about and use components is very different depending on whether they’re using events, two-way bindings, or one-way data flow with actions back up.

Here’s the thing: using a Dojo component is already possible in React today. In both Ember and React, the component has access to the raw DOM backing it. Because every framework uses the DOM as the underlying substrate of its components, it’s easy enough to use a Dojo component in React, or a React component from Ember.

The problem is that you are going to have to write code that maps React’s unidirectional data flow onto a component that expects two-way binding, or however it communicates. It’s not impossible or particularly hard, but it is annoying. Flat-out, it is just always going to be more convenient to use components that assume the same data flow as the rest of your app.

Here’s an analogy: imagine you’re writing an app that uses Promises everywhere. You pass Promises around for all of your asynchronous values, and everything is rosy.

Now you pull in three different libraries. One requires you to pass in callbacks, another returns RxJS Observables, and the third returns generator functions that you need to call .next() on to get the value.

Can all of these different styles of managing asynchrony be wrapped to map their values on to Promises? Yes. But all of that boilerplate will start to add up, both in code size and performance costs.

All things being equal, will your use of Promises make you more likely to use a library that returns Promises? Yes.

And often going “all in” on one particular system allows for system-wide optimizations. One of the things that makes rendering libraries like Glimmer fast is that they can analyze the entire component hierarchy to perform optimizations around propagating data changes. The second you add a foreign system into the mix, it can no longer reason about that part of the hierarchy; it’s a black box.

But the biggest fatal flaw here is that React, Ember, etc. give you a lot of nice, high-level API for building components. That code is worth it if it’s amortized over the entire application, but for an individual component?

If I move to a new framework and I want to bring a career’s worth of components with me, am I really willing to pay the cost of jQuery ∪ Backbone ∪ Angular ∪ Ember ∪ React? What if one of those components was written using Angular 1 and the other using Angular 2? As npm has shown us, transitive dependencies add up fast.

The only way to make this work, in my opinion, is to make writing Web Components feel as nice as writing React, Ember or Angular components, which is to say, unidirectional data flow with declarative rendering. Web Components are pretty far from that at the moment—they have lots of mutable state and communicate via events and attributes changing, and any updates to the DOM require imperative code. There are, generally speaking, lots of unresolved questions.

And should Web Components ever move to a more Ember/React/Angular-like model, by then, who knows if that will still be the way that web developers prefer to write their components? The second developers start preferring a new library to write their UI components, you’re back to square one.

Of course, you should never say never. It’s certainly a noble goal worth working towards. But in order for Web Components to achieve their goal of becoming the universal UI component, they’re going to have to become as good as what developers are used to today, and we’ll have to hope that React-style unidirectional data flow is the last major shift in how client-side UIs are architected.

--

--

Tom Dale

Member of the JavaScript glitterati.