npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@swyg/memo

v1.0.3

Published

💾 Memoize a function the way you want.

Downloads

6

Readme

memo

💾 Memoize a function the way you want.

⚡ Keep your JS/TS/React code running lightning fast and smooth!

🌍 Avoid wasting precious computation and worsen global warming.

⚠️ Not to be confused with the Spanish word "memo": https://m.interglot.com/es/en/Memo

Installation

npm install @swyg/memo

yarn install @swyg/memo

React's useMemo vs. @swyg/memo

With @swyg/memo you'll be able to enjoy a less verbose syntax and get more control over your memoization strategy.

For example, your component might re-render often and always call the same function with only a few combinations of params (e.g. filter or sort functionality). In that case, you could use memoCacheKey(...).

With React's useMemo:

const memoFunction = memoRef(myFunction);

const ReactComponent = ({ a, b, c }) => {
    const myResult = useMemo(() => myFunction(a, b, c), [a, b, c]);

    ...
}

With @swyg/memo:

const memoFunction = memoCacheKey(myFunction); // or memoLastKey or memoLastRef

const ReactComponent = ({ a, b, c }) => {
    const myResult = memoFunction(a, b, c);

    ...
}

However, keep in mind abusing memoization, especially when using memoCacheKey, can also have detrimental effects in your app's performance, as memory usage will increase.

Also, it's worth pointing out React's useMemo might eventually manage memory usage differently, as stated in the docs:

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

Usage

Functions:

Classes:

memoLastKey(func[, { resolver }]): MemoizedLastKeyFunction

Memoizes the result from the last call of the function func using the serialized arguments as the string key:

examples/last/key/basic.ts

const func = (value1: any, value2?: any) => {
    console.log('run');

    return { value1, value2 };
};

const memoFunc = memoLastKey(func);

const objA = { value: 'a' };
const objB = { value: 'b' };
const objC = { value: 'c' };

const a = func('foo'); // run
const b = memoFunc('foo'); // run
const c = memoFunc('foo');
const d = memoFunc('bar'); // run
const e = memoFunc('foo'); // run
const f = memoFunc(objA, objB); // run
const g = memoFunc(objA, objB); // NO run (incorrect)
const h = memoFunc(objA, objC); // NO run (incorrect)

console.log(a === b); // false
console.log(a === c); // false
console.log(b === c); // true
console.log(a === e); // false
console.log(b === e); // false
console.log(c === e); // false
console.log(f === g); // true
console.log(f === h); // true (incorrect)

A custom resolver function can be provided with the options param to use a custom cache identifier, which can be of any type:

examples/last/key/basic-resolver-str.ts

const func = (value: string | number) => {
    console.log('run');

    return { value };
};

const memoFunc = memoLastKey(func, {
    resolver: (...args) => args.map(arg => `${ arg }`).join('')
});

const a = memoFunc('1'); // run
const b = memoFunc(1);

console.log(a === b); // true

examples/last/key/basic-resolver-obj.ts

const func = (value: any) => {
    console.log('run');

    return { value };
};

const memoFunc = memoLastKey(func, {
    resolver: (arg: any) => arg.keyObj,
});

const keyObj = {};

const a = memoFunc({ keyObj }); // run
const b = memoFunc({ keyObj });

console.log(a === b); // true

By default, the following resolver function is used:

(...args) => `[${ args.map(arg => `${ typeof arg === 'string' ? `'${ arg }'` : arg }`).join(', ') }]`

Two additional properties, lastKey and lastValue, are attached to the memoized function:

examples/last/key/internals.ts

const func = (value: string) => ({ value });
const memoFunc = memoLastKey(func);

memoFunc('foo');

console.log(memoFunc.lastKey); // ['foo', 2, true, [object Object]]
console.log(memoFunc.lastValue); // foo

memoLastRef(func): MemoizedLastRefFunction

Memoizes the result from the last call of the function func until the arguments change:

examples/last/ref/basic.ts

const func = (value1: any, value2?: any) => {
    console.log('run');

    return { value1, value2 };
};

const memoFunc = memoLastRef(func);

const objA = { value: 'a' };
const objB = { value: 'b' };
const objC = { value: 'c' };

const a = func('foo'); // run
const b = memoFunc('foo'); // run
const c = memoFunc('foo');
const d = memoFunc('bar'); // run
const e = memoFunc('foo'); // run
const f = memoFunc(objA, objB); // run
const g = memoFunc(objA, objB);
const h = memoFunc(objA, objC); // run

console.log(a === b); // false
console.log(a === c); // false
console.log(b === c); // true
console.log(a === e); // false
console.log(b === e); // false
console.log(c === e); // false
console.log(f === g); // true
console.log(f === h); // false

Two additional properties, lastArgs and lastValue, are attached to the memoized function:

examples/last/ref/internals.ts

const func = (...args: any) => 'foo';
const memoFunc = memoLastRef(func);

memoFunc('foo', 2, true, { a: 1 });

console.log(memoFunc.lastArgs); // [ 'foo', 2, true, { a: 1 } ]
console.log(memoFunc.lastValue); // foo

memoCacheKey(func[, { resolver }]): MemoizedCacheFunction

Memoizes the result from the N most recent calls of the function func using the serialized arguments as the string key:

examples/cache/basic.ts

const func = (value1: any, value2?: any) => {
    console.log('run');

    return { value1, value2 };
};

const memoFunc = memoCacheKey(func, { slots: 2 });

const a = func('foo'); // run
const b = memoFunc('foo'); // run
const c = memoFunc('foo');
const d = memoFunc('bar'); // run
const e = memoFunc('foo'); 
const f = memoFunc('bar'); 
const g = memoFunc('baz'); // run
const h = memoFunc('foo'); // run

console.log(a === b); // false
console.log(a === c); // false
console.log(b === c); // true
console.log(b === e); // true
console.log(c === e); // true
console.log(e === h); // false

An additional property, cache, is attached to the memoized function:

examples/cache/internals.ts

const func = (value1: any, value2?: any) => {
    console.log('run');

    return { value1, value2 };
};

const memoFunc = memoCacheKey(func, { slots: 2 });

memoFunc('foo'); 
memoFunc('bar'); 
memoFunc('baz'); 

console.log(memoFunc.cache); // CacheSet [Map] { ... }

CacheSet(iterableOrObject?: Iterable<[K, V]> | CacheSetOptions, options?: CacheSetOptions)

Create an instance of a set with a maximum number of elements (options.slots), limited time-to-live (options.ttl) or the ability to update the stored value when Promises are resolved (options.keepPromises):

const cacheSet = new CacheSet<number, string>([[1, 'one'], [2, 'two']], { slots: 2});

console.log(cacheSet.size); // 2

cacheSet.set(3, 'three');

console.log(cacheSet.size); // 2

console.log(cacheSet.get(1)); // undefined

TypeScript Support

Types are already included in this package.

You can check if a variable is referencing a memoized function with the following type guards:

TODO

  • Implement a mechanism to skip the memoization (e.g. return undefined for the cache key or attach an withoutMemo() function to the memoized function).