UNPKG

mobx-utils

Version:

Utility functions and common patterns for MobX

101 lines (99 loc) 3.92 kB
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(); }; }