UNPKG

3.82 kBJavaScriptView Raw
1var __assign = (this && this.__assign) || function () {
2 __assign = Object.assign || function(t) {
3 for (var s, i = 1, n = arguments.length; i < n; i++) {
4 s = arguments[i];
5 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6 t[p] = s[p];
7 }
8 return t;
9 };
10 return __assign.apply(this, arguments);
11};
12var __spreadArrays = (this && this.__spreadArrays) || function () {
13 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
14 for (var r = Array(s), k = 0, i = 0; i < il; i++)
15 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
16 r[k] = a[j];
17 return r;
18};
19import { DeepMap } from "./deepMap";
20import { computed, onBecomeUnobserved, _isComputingDerivation, isAction, _getGlobalState, } from "mobx";
21/**
22 * computedFn takes a function with an arbitrary amount of arguments,
23 * and memoizes the output of the function based on the arguments passed in.
24 *
25 * computedFn(fn) returns a function with the very same signature. There is no limit on the amount of arguments
26 * that is accepted. However, the amount of arguments must be constant and default arguments are not supported.
27 *
28 * By default the output of a function call will only be memoized as long as the
29 * output is being observed.
30 *
31 * The function passes into `computedFn` should be pure, not be an action and only be relying on
32 * observables.
33 *
34 * Setting `keepAlive` to `true` will cause the output to be forcefully cached forever.
35 * Note that this might introduce memory leaks!
36 *
37 * @example
38 * const store = observable({
39 a: 1,
40 b: 2,
41 c: 3,
42 m: computedFn(function(x) {
43 return this.a * this.b * x
44 })
45 })
46
47 const d = autorun(() => {
48 // store.m(3) will be cached as long as this autorun is running
49 console.log(store.m(3) * store.c)
50 })
51 *
52 * @param fn
53 * @param keepAliveOrOptions
54 */
55export function computedFn(fn, keepAliveOrOptions) {
56 if (keepAliveOrOptions === void 0) { keepAliveOrOptions = false; }
57 if (isAction(fn))
58 throw new Error("computedFn shouldn't be used on actions");
59 var memoWarned = false;
60 var i = 0;
61 var opts = typeof keepAliveOrOptions === "boolean"
62 ? { keepAlive: keepAliveOrOptions }
63 : keepAliveOrOptions;
64 var d = new DeepMap();
65 return function () {
66 var _this = this;
67 var args = [];
68 for (var _i = 0; _i < arguments.length; _i++) {
69 args[_i] = arguments[_i];
70 }
71 var entry = d.entry(args);
72 // cache hit, return
73 if (entry.exists())
74 return entry.get().get();
75 // if function is invoked, and its a cache miss without reactive, there is no point in caching...
76 if (!opts.keepAlive && !_isComputingDerivation()) {
77 if (!memoWarned && _getGlobalState().computedRequiresReaction) {
78 console.warn("Invoking a computedFn from outside a reactive context won't be memoized unless keepAlive is set.");
79 memoWarned = true;
80 }
81 return fn.apply(this, args);
82 }
83 // create new entry
84 var latestValue;
85 var c = computed(function () {
86 return (latestValue = fn.apply(_this, args));
87 }, __assign(__assign({}, opts), { name: "computedFn(" + (opts.name || fn.name) + "#" + ++i + ")" }));
88 entry.set(c);
89 // clean up if no longer observed
90 if (!opts.keepAlive)
91 onBecomeUnobserved(c, function () {
92 d.entry(args).delete();
93 if (opts.onCleanup)
94 opts.onCleanup.apply(opts, __spreadArrays([latestValue], args));
95 latestValue = undefined;
96 });
97 // return current val
98 return c.get();
99 };
100}