How To Transparently Turn A Native JavaScript Variable Into A Reactive Meteor Variable?
Solution 1:
The two cases, native variable and object field, need to be taken separately, they will require different approaches. The first one will use a simple yet dirty trick, the second a more advanced trick.
Let's start with the native variable case.
The object field
If the variable is a writable object field, then we can change the reference to make a custom get/set couple linked to a reactive variable in a closure.
It's as simple as that:
function reactivise(obj, field) {
var rvar = new ReactiveVar(obj[field]);
Object.defineProperty(obj, field, {
get : function() {
return rvar.get();
},
set : function(value) {
rvar.set(value);
return value;
}
})
}
And it just works. Native code using obj.foo
won't notice a change (unless they check for the property descriptors but that's a weird thing to do). Reactive computations however will be invalidated by changes to this field.
However, it is weak against the module pattern (reference isolation to prevent corruption). Here's an example of such module :
(function logFoo(foo) {
console.log(foo);
}(obj.foo);
This code won't care that you changed the getter or the setter, it already holds the reference.
There might be a way around this... But as of this writing it's pretty much alchemy. An ES7 feature that could help: Object.observe
. It is so young today that I won't draw an example out of it.
The variable
If what you want to observe is not a non-moduled object field (example above), then the only solution I know of is polling.
Basically, regularly check if the value changed, and set a new reactive variable for that (we lose the transparency).
Example of such polling:
function reactivePoll(getter) {
var rPoll = new ReactiveVar(getter());
Meteor.setInterval(function pollVariable() {
var newValue = getter();
if(!_.isEqual(rPoll.curValue, newValue)) {
rPoll.set(newValue);
}
}, 100);
return rPoll;
}
What we need for it to work is not the variable reference in itself (foo
), but a getter to this variable. This is because if the foo
reference is later changed in the code our function won't be aware of it (still more painful desynchronized headaches).
Plus, we have to check for deep equality each time to make sure that the value did indeed change before we cause invalidations since Tracker will automatically invalidate if it sees a non-primitive value.
An example of use:
var reactiveFoo = reactivePoll(function getFoo() { return foo; });
It of course also works with object fields.
Note that the example code does not feature any kind of stop mechanism. It will run forever, may provoke memory leaks, crash, slow down your apps, and cause violent head pain. Do not use it as such in production applications, adapt it to add better control over the intervals.
The safest bet is then the basic dirty polling, even if it means a bit more load and a complete control needed to prevent memory leaks.
Post a Comment for "How To Transparently Turn A Native JavaScript Variable Into A Reactive Meteor Variable?"