Create a fully Reactive UI Framework with JavaScript Proxies and Virtual DOM

Francesco Strazzullo
Flowing
Published in
5 min readNov 6, 2016

Recently I got interested in the Reactive Programming world, especially in the MobX ecosystem. I really like the idea behind this framework: to be reactive in a transparent way. So I asked myself…

What does it take to create a fully Reactive (and transparent) UI framework in JavaScript?

Don’t worry, you will not see another JavaScript framework on the npm registry (at least for now). But I think that this is a good architectural exercise. We’re going to split the problem in two parts. The first one is a UI library that helps us render our state to the DOM, the second one is a reactive state management library. Yes, we are going to create a shitty version of React+MobX stack. :)

UI Framework

At a high level of abstraction a UI framework should be just a pure function of the state of our application. To express this concept in a mathematical way…

If we want to have an highly performant rendering without all the fuss of a complete library like React, we can just use a pure implementation of a virtual-dom algorithm, like the one that you can find on this GitHub repository by @MatthewEsch. To render a simple list from an array we can write:

import { create, h } from 'virtual-dom';

const render = (state) => {
const children = state.list.map(t => h('li',{},[t]));
return h('ul', {}, children);
};

const INITIAL_STATE = {
list:['first','second']
};

let tree = render(INITIAL_STATE);
let rootNode = create(tree);

document.body.appendChild(rootNode);

As you can see this virtual-dom implementation uses the HyperScript format to define HTML elements. When the list changes we need to patch our DOM in this way:

const updateDom = (state) => {
const newTree = render(state);
const patches = diff(tree, newTree);

tree = newTree;
rootNode = patch(rootNode, patches);
};

Ok so we have our pure functions to create and update our DOM tree. In other words, the UI part of our framework is completed. Let’s talk about the state management part.

Reactive State Management Library

The state management library needs to be Reactive. But what is the meaning of “Be Reactive”? In my opinion, the simplest way to define a Reactive application is…

Obviously I’m oversimplifying here, but in the end Reactive Programming it’s all about Observables. My purpose here is to create a reactive state management library that is also transparent for the user of the framework itself. Just like in a MobX application I want to re-render my application just changing the model. So to add a new element to the list I just want to write something like this:

state.list = […state.list,’Another Element’];

The fastest way that I know in JavaScript to achieve this objective is to use the EcmaScript 2015 Proxies. The MDN definition of ‘Proxy’ states that:

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

Ok, before we start digging in the code, consider that Object Proxies are not supported by all the browsers. You can use this plugin for Babel or a simple polyfill created by the Google Chrome team.

Oh you IE…

To create a Proxy we can just use the Proxy constructor. In the next example we create a simple ‘Loggable’ object factory that prints on the console every property lookup or assignment that we do on the target object.

export default (target) => {
const loggingHandler = {
get: function (target,name) {
const value = target[name];
console.log(`getting ${name}: ${value}`);
return value;
},
set: function (target,name,value) {
console.log(`setting ${name}: ${value}`);
target[name] = value;
return true;
}
};

return new Proxy(target,loggingHandler);
};

Then we can create a ‘loggable’ object in a very simple way:

const INITIAL_STATE = {
startValue:0
};

const state = loggable(INITIAL_STATE);

const value = state.startValue; //prints 'getting startValue: 0'

state.startValue = 1; //prints 'setting startValue: 1'

We can use the same technique to create a generic Observable, we just need a target object and a listener callback.

export default ({target,listener}) => {
let observable;

const set = (target,name,value) => {
target[name] = value;
listener(observable);
return true;
};

const get = (target,name) => {
return Object.freeze(target[name]);
};

const handler = {
set,
get
};

observable = new Proxy(target,handler);

return observable;
};

Now that we have all the pieces of our framework we just need to put that in place.

import { patch, create, diff } from 'virtual-dom';
import { render } from './view';
import loggable from './loggable';
import observable from './observable';

const updateDom = (state) => {
const newTree = render(state);
const patches = diff(tree, newTree);

tree = newTree;
rootNode = patch(rootNode, patches);
};

const INITIAL_STATE = {
//The state of your application
};

const state = observable({
target:loggable(INITIAL_STATE),
listener:updateDom
});

let tree = render(state);
let rootNode = create(tree);

document.body.appendChild(rootNode);

And that’s it! We just render our initial DOM with the render function, and then we update it automatically (or I should say transparently?) just changing the ‘state’ variable. Here you can find a live demo of a todo list built with this approach. The source code is available on my GitHub account.

Conclusions

Obviously this is not a real framework, but I think that sometimes it’s useful to “reinvent the wheel” on your own. Because in some contexts you should consider not to use a framework but to start from scratch, in order to keep your technical debt at a minimal level. This is also the reason because I love the JavaScript ecosystem. It’s true that we have a new ‘shiny’ framework every week, but it’s not a fatigue, it’s a great opportunity to learn new ways to write and organize our code.

Post Scriptum

If you’re interested in the Reactive Programming and MobX Development you can check my talk “Stay (React)ive with MobX” at ReactJS Day in Verona. I talked about MobX and the meaning of “Be Reactive”.

If you liked this post, you can buy me a virtual coffee ️😊

Buy me a Coffee

--

--

Francesco Strazzullo
Flowing

Partner @ flowing. Author of “Frameworkless Front-End Development” and “Decision-making for Software Development Teams”