A memo library which can be used in many cases, this library is inspired by memoize-one
, proxy-memoize
, async-memoize-one
, and have more feature to support different use cases.
memoize-one
and proxy-memoize
are great, but they are only for some general cases, as many tools and libraries are designed in a special way, so some corner case can't be handled correctly, so this library build up for solving this issue.
memoize-one
: use shallow compare as a straightforward way.proxy-memoize
: is build onproxy-compare
which author designed as a stateless library, andproxy-memoize
use its markup key to compare old object and new object.
As many library now use the Proxy
feature, and Proxy
always work with Scope
feature, so we can't always simply treat object as a immutable value, especially in some library design, maybe that's we wanted, so This library give an another choice to control the logic, not just a dead code.
npm install @fantastic-utils/memo
this library contains memo
and memoAsync
.
import { memo, memoAsync, original } from '@fantastic-utils/memo';
// Basic cache
const add = (a, b) => a + b;
const memoAdd = memo(add);
memoAdd(1, 2); // 3
memoAdd(1, 2); // 3 from cache
// Deep cache
const add = (objA, arrayB) => objA.a + objB[0];
const memoAdd = memo(add);
memoAdd({ a: 1 }, [2]); // 3
memoAdd({ a: 1 }, [2]); // 3 from cache
// Async cache
const add = async (a, b) => await (a + b);
const memoAdd = memoAsync(add);
await memoAdd(1, 2); // 3
await memoAdd(1, 2); // 3
// Shallow compare
const add = (objA, b) => objA.a + b;
const memoAdd = memo(add, { objectShallowCompare: true });
const obj = { a: 1 };
memoAdd(obj, 2); // 3
obj.a = 2;
memoAdd(obj, 2); // 3 from cache
// console log raw data, as argument is wrapped by proxy
const add = (objA, b) => {
console.log(original(objA));
return objA.a + b;
};
import { memo, original } from '@fantastic-utils/memo';
const TestComponent = memo((props) => {
// Take care of console.log, as console.log will construct proxy values
// use original to get the raw value.
// Note that `.detail` will be marked as used
console.log(original(props.detail));
return (
<>
<h3>{props.detail.address}</h3>
</>
);
});
export default TestComponent;
// memo function
memo(fn, memo: MemoConfig): fn
// async memo function
memoAsync(fn, memo: MemoConfig): fn
// Get raw object, as arg are wrapped by proxy
original(arg): object
export interface MemoConfig {
objectShallowCompare?: boolean;
shouldCompare?: (args?: any[], cachedArgsCfg?: NormalizeArgCfg[]) => boolean;
isChanged?: (args?: any[], cachedArgsCfg?: NormalizeArgCfg[]) => boolean;
}
/**
* @param t The Type
* @param v The normalized value
* @param r The raw value
* @param a The affected info
*/
export interface NormalizeArgCfg {
t: string;
v: unknown;
r: unknown;
a: Affected;
}
API: function(newArgs: any[], cachedArgsCfg: NormalizeArgCfg[]): boolean
.
shouldCompare
let user to determine whether to compare args with default compare logic or not,
import { memo } from '@fantastic-utils/memo';
const add = (a, b) => a + b;
const memoAdd = memo(add, {
shouldCompare(newArgs, cachedArgCfg) {
return newArgs[0] === 1;
},
});
memoAdd(1, 2); // 3
memoAdd(1, 2); // 3 from cache
expect(memoAdd).toBeCalledTimes(1); // only call once
API: function(newArgs: any[], cachedArgsCfg: NormalizeArgCfg[]): boolean
.
isChanged
is a high level api than shouldCompare
to control all compare logic.
import { memo } from '@fantastic-utils/memo';
const add = (a, b) => a + b;
const memoAdd = memo(add, {
isChanged(newArgs, cachedArgCfg) {
return newArgs[0] === 1;
},
});
memoAdd(1, 2); // 3
memoAdd(1, 2); // 3 from re-run function
expect(memoAdd).toBeCalledTimes(2); // call twice
The biggest difference between shouldCompare
and isChanged
is that shouldCompare
control how to use cache, and isChanged
control how to compare. sometimes shouldCompare
used to improve performance, and isChanged
used to customize compare logic.
Please refer to above code see the compare detail.
Benchmark code pls refer to __benchmark__
folder.
@fantastic-utils/memo deep x 82,670,951 ops/sec ±2.32% (85 runs sampled)
proxy-memoize deep x 779,043 ops/sec ±3.83% (85 runs sampled)
memoize-state deep x 22,624,061 ops/sec ±1.82% (81 runs sampled)
Fastest is @fantastic-utils/memo deep
@fantastic-utils/memo shallow x 26,180,311 ops/sec ±1.04% (89 runs sampled)
proxy-memoize shallow x 1,120,131 ops/sec ±1.29% (86 runs sampled)
memoize-one shallow x 17,569,314 ops/sec ±1.66% (83 runs sampled)
memoize-state shallow x 18,697,192 ops/sec ±3.24% (86 runs sampled)
Fastest is @fantastic-utils/memo shallow
$ npm install
$ npm run dev
$ npm run build
MIT