Reactive Box

Today I had great success with implementing some basic reactive functionality.

At the basis of the editor UI there is a really crude reactive library I wrote. The idea is that you create boxes for values, and you call a getter to retrieve the value, but every time you call it, a dependency is recorded. There’s also a compute function which takes a computation function as argument, and takes the dependencies recorded during the execution of the computation and sets things up so when any dependency box’s content changes the whole computation function is re-run. This computation function should have some sort of side-effect, e.g. setting another box’s value or updating a DOM element.

This method results in a lot of noise in the code for setting up each box. Now, as I’m in the process of reimplementing the UI with itself, it occurred to me that I can just add a new language element for defining a box. I can set the type of a class property to a box and then just use it as a normal variable, and let the transpiler insert all the repetitive code that makes it work.

This now works for my little contrived example: I have a class with three reactive box properties; x is initialised through the constructor and can also be set from the outside. Whenever x changes, y will be automatically set to x*5. When y changes, z is set to the string "Z:"+y. Whenever z changes, the compute block on the top updates the DOM. As I set the value of x to 0, 1, 2, the DOM shows Z:0, Z:5, Z:10, etc.

Whilelse code rendering:

Generated JavaScript code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
function box() {
return require('lib/box').box.apply(this, arguments);
}
function compute() {
return require('lib/box').compute.apply(this, arguments);
}
var Calculator = function () {
Calculator.displayName = 'Calculator';
var prototype = Calculator.prototype;
function Calculator(x) {
var this$ = this;
this$.x$box = box('acinqOszyXna', undefined);
this$.y$box = box('acOiZUpDkUA2', undefined);
this$.z$box = box('acOYmFYEVhx3', undefined);
this$.x$set(x);
}
prototype.x$get = function () {
var this$ = this;
return this$.x$box.get();
};
prototype.x$set = function (value) {
this$ = this;
this$.x$box.set(value);
};
prototype.y$get = function () {
var this$ = this;
if (!this$.y$boxInited) {
this$.y$boxInited = true;
compute('acOiZUpDkUA2', function () {
this$.y$box.set(function () {
return this$.x$get() * 5;
}());
});
}
return this$.y$box.get();
};
prototype.z$get = function () {
var this$ = this;
if (!this$.z$boxInited) {
this$.z$boxInited = true;
compute('acOYmFYEVhx3', function () {
this$.z$box.set(function () {
return 'Z:' + this$.y$get();
}());
});
}
return this$.z$box.get();
};
return Calculator;
}();
function display(x) {
var el;
el = document.getElementsByTagName('body')[0];
el.innerHTML = '';
el.appendChild(document.createTextNode('' + x));
}
module.exports = function () {
var calc;
var num;
num = 0;
calc = new Calculator(num);
compute('ac48AuoEg1PD', function () {
display(calc.z$get());
});
;
setInterval(function () {
calc.x$set(num++);
}, 1000);
}();