mobx-utils
Version:
Utility functions and common patterns for MobX
101 lines (99 loc) • 3.92 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
import { DeepMap } from "./deepMap";
import { computed, onBecomeUnobserved, _isComputingDerivation, isAction, _getGlobalState, } from "mobx";
/**
* computedFn takes a function with an arbitrary amount of arguments,
* and memoizes the output of the function based on the arguments passed in.
*
* computedFn(fn) returns a function with the very same signature. There is no limit on the amount of arguments
* that is accepted. However, the amount of arguments must be constant and default arguments are not supported.
*
* By default the output of a function call will only be memoized as long as the
* output is being observed.
*
* The function passes into `computedFn` should be pure, not be an action and only be relying on
* observables.
*
* Setting `keepAlive` to `true` will cause the output to be forcefully cached forever.
* Note that this might introduce memory leaks!
*
* @example
* const store = observable({
a: 1,
b: 2,
c: 3,
m: computedFn(function(x) {
return this.a * this.b * x
})
})
const d = autorun(() => {
// store.m(3) will be cached as long as this autorun is running
console.log(store.m(3) * store.c)
})
*
* @param fn
* @param keepAliveOrOptions
*/
export function computedFn(fn, keepAliveOrOptions) {
if (keepAliveOrOptions === void 0) { keepAliveOrOptions = false; }
if (isAction(fn))
throw new Error("computedFn shouldn't be used on actions");
var memoWarned = false;
var i = 0;
var opts = typeof keepAliveOrOptions === "boolean"
? { keepAlive: keepAliveOrOptions }
: keepAliveOrOptions;
var d = new DeepMap();
return function () {
var _this = this;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var entry = d.entry(args);
// cache hit, return
if (entry.exists())
return entry.get().get();
// if function is invoked, and its a cache miss without reactive, there is no point in caching...
if (!opts.keepAlive && !_isComputingDerivation()) {
if (!memoWarned && _getGlobalState().computedRequiresReaction) {
console.warn("invoking a computedFn from outside an reactive context won't be memoized, unless keepAlive is set");
memoWarned = true;
}
return fn.apply(this, args);
}
// create new entry
var latestValue;
var c = computed(function () {
return (latestValue = fn.apply(_this, args));
}, __assign(__assign({}, opts), { name: "computedFn(" + (opts.name || fn.name) + "#" + ++i + ")" }));
entry.set(c);
// clean up if no longer observed
if (!opts.keepAlive)
onBecomeUnobserved(c, function () {
d.entry(args).delete();
if (opts.onCleanup)
opts.onCleanup.apply(opts, __spreadArrays([latestValue], args));
latestValue = undefined;
});
// return current val
return c.get();
};
}