A memoization library which only remembers the latest invocation
npx @tessl/cli install tessl/npm-memoize-one@6.0.0Memoize One is a lightweight memoization library that implements a unique single-cache strategy, storing only the most recent function call result and arguments. Unlike traditional memoization libraries that maintain multiple cached entries with complex cache management, memoize-one simplifies memory usage by remembering only the latest invocation, automatically discarding previous results when new arguments are provided.
npm install memoize-oneimport memoizeOne from "memoize-one";
import type { EqualityFn, MemoizedFn } from "memoize-one";For CommonJS:
const memoizeOne = require("memoize-one");import memoizeOne from "memoize-one";
// Simple function memoization
function add(a: number, b: number): number {
return a + b;
}
const memoizedAdd = memoizeOne(add);
memoizedAdd(1, 2); // Function called, returns 3
memoizedAdd(1, 2); // Cache hit, returns 3 (function not called)
memoizedAdd(2, 3); // New arguments, function called, returns 5
memoizedAdd(1, 2); // Cache miss (not the latest), function called again
// Clear cache manually
memoizedAdd.clear();Memoize One uses a simple, memory-efficient architecture:
this context changes by invalidating the cacheCreates a memoized version of any function that remembers only the latest invocation result.
/**
* Creates a memoized version of the provided function
* @param resultFn - The function to be memoized
* @param isEqual - Optional custom equality function for comparing arguments
* @returns Memoized function with clear() method
*/
function memoizeOne<TFunc extends (this: any, ...newArgs: any[]) => any>(
resultFn: TFunc,
isEqual?: EqualityFn<TFunc>
): MemoizedFn<TFunc>;Usage Examples:
import memoizeOne from "memoize-one";
// Basic memoization
const expensiveCalculation = (x: number, y: number) => {
console.log("Computing...");
return Math.pow(x, y);
};
const memoized = memoizeOne(expensiveCalculation);
memoized(2, 8); // Logs "Computing...", returns 256
memoized(2, 8); // No log, returns 256 from cache
// With custom equality function
import isDeepEqual from "lodash.isequal";
const processObjects = (data: any[]) => data.map(item => ({ ...item, processed: true }));
const memoizedProcess = memoizeOne(processObjects, isDeepEqual);
memoizedProcess([{ id: 1 }]); // Function called
memoizedProcess([{ id: 1 }]); // Cache hit with deep equalityDefines how arguments are compared to determine cache hits.
/**
* Function type for custom argument equality comparison
* @param newArgs - New function arguments
* @param lastArgs - Previously cached arguments
* @returns True if arguments are considered equal (cache hit)
*/
type EqualityFn<TFunc extends (...args: any[]) => any> = (
newArgs: Parameters<TFunc>,
lastArgs: Parameters<TFunc>
) => boolean;Usage Examples:
import memoizeOne, { EqualityFn } from "memoize-one";
// Shallow equality for array contents
const shallowArrayEqual: EqualityFn<(arr: number[]) => number> = (newArgs, lastArgs) => {
const [newArr] = newArgs;
const [lastArr] = lastArgs;
return newArr.length === lastArr.length &&
newArr.every((val, idx) => val === lastArr[idx]);
};
const sumArray = (arr: number[]) => arr.reduce((sum, val) => sum + val, 0);
const memoizedSum = memoizeOne(sumArray, shallowArrayEqual);
memoizedSum([1, 2, 3]); // Function called
memoizedSum([1, 2, 3]); // Cache hit (shallow equal)The enhanced function returned by memoizeOne with additional cache management.
/**
* Enhanced function interface with cache management
*/
type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
/** Clears the cached result and arguments */
clear(): void;
/** The original function signature with preserved context and types */
(this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
};Usage Examples:
import memoizeOne from "memoize-one";
const fibonacci = (n: number): number => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
const memoizedFib = memoizeOne(fibonacci);
memoizedFib(10); // Computed
memoizedFib(10); // From cache
// Clear cache when needed
memoizedFib.clear();
memoizedFib(10); // Computed again
// Works with context (this)
class Calculator {
multiplier = 2;
multiply = memoizeOne((value: number) => {
return value * this.multiplier;
});
}
const calc = new Calculator();
calc.multiply(5); // Returns 10
calc.multiply(5); // From cacheBy default, memoize-one uses shallow equality comparison with special NaN handling:
=== comparisonNaN values are considered equal (unlike normal ===)this context automatically invalidate cacheExamples:
const memoized = memoizeOne((a, b) => a + b);
// Same arguments - cache hit
memoized(1, 2); // Computed
memoized(1, 2); // Cache hit
// Different argument count - cache miss
memoized(1, 2, 3); // Computed (different arg count)
// NaN handling
const memoizedNaN = memoizeOne((x) => x);
memoizedNaN(NaN); // Computed
memoizedNaN(NaN); // Cache hit (NaN === NaN in memoize-one)
// Object references
memoized({ id: 1 }, { id: 2 }); // Computed
memoized({ id: 1 }, { id: 2 }); // Cache miss (different object references)When the memoized function throws an error, memoize-one handles it with specific behavior:
Examples:
import memoizeOne from "memoize-one";
const canThrow = (shouldThrow: boolean, value: number) => {
if (shouldThrow) {
throw new Error("Something went wrong");
}
return value * 2;
};
const memoized = memoizeOne(canThrow);
// Successful call - result cached
const result1 = memoized(false, 5); // Returns 10, cached
// Exception call - no caching
try {
memoized(true, 3); // Throws Error, not cached
} catch (e) {
console.log("First error:", e.message);
}
// Same exception call - function called again (no cache)
try {
memoized(true, 3); // Throws Error again, function re-executed
} catch (e) {
console.log("Second error:", e.message);
}
// Previous successful result still cached
const result2 = memoized(false, 5); // Returns 10 from cache (not re-executed)
console.log(result1 === result2); // true