framework/js/src/common/utils/SubtreeRetainer.js

69 lines
1.5 KiB
JavaScript
Raw Normal View History

/**
* The `SubtreeRetainer` class represents a Mithril virtual DOM subtree. It
* keeps track of a number of pieces of data, allowing the subtree to be
* retained if none of them have changed.
*
* @example
* // constructor
* this.subtree = new SubtreeRetainer(
* () => this.props.post.freshness,
* () => this.showing
* );
* this.subtree.check(() => this.props.user.freshness);
*
* // view
* this.subtree.retain() || 'expensive expression'
*
* @see https://lhorie.github.io/mithril/mithril.html#persisting-dom-elements-across-route-changes
*/
export default class SubtreeRetainer {
/**
* @param {...callbacks} callbacks Functions returning data to keep track of.
*/
constructor(...callbacks) {
this.callbacks = callbacks;
this.data = {};
}
/**
* Return a virtual DOM directive that will retain a subtree if no data has
* changed since the last check.
*
* @return {Object|false}
* @public
*/
retain() {
let needsRebuild = false;
this.callbacks.forEach((callback, i) => {
const result = callback();
if (result !== this.data[i]) {
this.data[i] = result;
needsRebuild = true;
}
});
return needsRebuild ? false : {subtree: 'retain'};
}
/**
* Add another callback to be checked.
*
* @param {...Function} callbacks
* @public
*/
check(...callbacks) {
this.callbacks = this.callbacks.concat(callbacks);
}
/**
* Invalidate the subtree, forcing it to be rerendered.
*
* @public
*/
invalidate() {
this.data = {};
}
}