0
# Memoize One
1
2
Memoize 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.
3
4
## Package Information
5
6
- **Package Name**: memoize-one
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install memoize-one`
10
11
## Core Imports
12
13
```typescript
14
import memoizeOne from "memoize-one";
15
import type { EqualityFn, MemoizedFn } from "memoize-one";
16
```
17
18
For CommonJS:
19
20
```javascript
21
const memoizeOne = require("memoize-one");
22
```
23
24
## Basic Usage
25
26
```typescript
27
import memoizeOne from "memoize-one";
28
29
// Simple function memoization
30
function add(a: number, b: number): number {
31
return a + b;
32
}
33
34
const memoizedAdd = memoizeOne(add);
35
36
memoizedAdd(1, 2); // Function called, returns 3
37
memoizedAdd(1, 2); // Cache hit, returns 3 (function not called)
38
memoizedAdd(2, 3); // New arguments, function called, returns 5
39
memoizedAdd(1, 2); // Cache miss (not the latest), function called again
40
41
// Clear cache manually
42
memoizedAdd.clear();
43
```
44
45
## Architecture
46
47
Memoize One uses a simple, memory-efficient architecture:
48
49
- **Single Cache Strategy**: Only stores the most recent function call result, arguments, and context
50
- **Automatic Cache Invalidation**: Previous results are automatically discarded when new arguments are provided
51
- **Context Sensitivity**: Handles `this` context changes by invalidating the cache
52
- **Custom Equality**: Supports custom argument comparison functions for complex data types
53
- **Type Safety**: Full TypeScript support with generic function signature preservation
54
55
## Capabilities
56
57
### Memoization Function
58
59
Creates a memoized version of any function that remembers only the latest invocation result.
60
61
```typescript { .api }
62
/**
63
* Creates a memoized version of the provided function
64
* @param resultFn - The function to be memoized
65
* @param isEqual - Optional custom equality function for comparing arguments
66
* @returns Memoized function with clear() method
67
*/
68
function memoizeOne<TFunc extends (this: any, ...newArgs: any[]) => any>(
69
resultFn: TFunc,
70
isEqual?: EqualityFn<TFunc>
71
): MemoizedFn<TFunc>;
72
```
73
74
**Usage Examples:**
75
76
```typescript
77
import memoizeOne from "memoize-one";
78
79
// Basic memoization
80
const expensiveCalculation = (x: number, y: number) => {
81
console.log("Computing...");
82
return Math.pow(x, y);
83
};
84
85
const memoized = memoizeOne(expensiveCalculation);
86
memoized(2, 8); // Logs "Computing...", returns 256
87
memoized(2, 8); // No log, returns 256 from cache
88
89
// With custom equality function
90
import isDeepEqual from "lodash.isequal";
91
92
const processObjects = (data: any[]) => data.map(item => ({ ...item, processed: true }));
93
const memoizedProcess = memoizeOne(processObjects, isDeepEqual);
94
95
memoizedProcess([{ id: 1 }]); // Function called
96
memoizedProcess([{ id: 1 }]); // Cache hit with deep equality
97
```
98
99
### Custom Equality Function
100
101
Defines how arguments are compared to determine cache hits.
102
103
```typescript { .api }
104
/**
105
* Function type for custom argument equality comparison
106
* @param newArgs - New function arguments
107
* @param lastArgs - Previously cached arguments
108
* @returns True if arguments are considered equal (cache hit)
109
*/
110
type EqualityFn<TFunc extends (...args: any[]) => any> = (
111
newArgs: Parameters<TFunc>,
112
lastArgs: Parameters<TFunc>
113
) => boolean;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import memoizeOne, { EqualityFn } from "memoize-one";
120
121
// Shallow equality for array contents
122
const shallowArrayEqual: EqualityFn<(arr: number[]) => number> = (newArgs, lastArgs) => {
123
const [newArr] = newArgs;
124
const [lastArr] = lastArgs;
125
return newArr.length === lastArr.length &&
126
newArr.every((val, idx) => val === lastArr[idx]);
127
};
128
129
const sumArray = (arr: number[]) => arr.reduce((sum, val) => sum + val, 0);
130
const memoizedSum = memoizeOne(sumArray, shallowArrayEqual);
131
132
memoizedSum([1, 2, 3]); // Function called
133
memoizedSum([1, 2, 3]); // Cache hit (shallow equal)
134
```
135
136
### Memoized Function Interface
137
138
The enhanced function returned by memoizeOne with additional cache management.
139
140
```typescript { .api }
141
/**
142
* Enhanced function interface with cache management
143
*/
144
type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
145
/** Clears the cached result and arguments */
146
clear(): void;
147
/** The original function signature with preserved context and types */
148
(this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
149
};
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import memoizeOne from "memoize-one";
156
157
const fibonacci = (n: number): number => {
158
if (n <= 1) return n;
159
return fibonacci(n - 1) + fibonacci(n - 2);
160
};
161
162
const memoizedFib = memoizeOne(fibonacci);
163
164
memoizedFib(10); // Computed
165
memoizedFib(10); // From cache
166
167
// Clear cache when needed
168
memoizedFib.clear();
169
memoizedFib(10); // Computed again
170
171
// Works with context (this)
172
class Calculator {
173
multiplier = 2;
174
175
multiply = memoizeOne((value: number) => {
176
return value * this.multiplier;
177
});
178
}
179
180
const calc = new Calculator();
181
calc.multiply(5); // Returns 10
182
calc.multiply(5); // From cache
183
```
184
185
## Default Equality Behavior
186
187
By default, memoize-one uses shallow equality comparison with special NaN handling:
188
189
1. **Argument Count**: Arguments must have the same length
190
2. **Strict Equality**: Each argument uses `===` comparison
191
3. **NaN Special Case**: Two `NaN` values are considered equal (unlike normal `===`)
192
4. **Context Sensitivity**: Changes to `this` context automatically invalidate cache
193
194
**Examples:**
195
196
```typescript
197
const memoized = memoizeOne((a, b) => a + b);
198
199
// Same arguments - cache hit
200
memoized(1, 2); // Computed
201
memoized(1, 2); // Cache hit
202
203
// Different argument count - cache miss
204
memoized(1, 2, 3); // Computed (different arg count)
205
206
// NaN handling
207
const memoizedNaN = memoizeOne((x) => x);
208
memoizedNaN(NaN); // Computed
209
memoizedNaN(NaN); // Cache hit (NaN === NaN in memoize-one)
210
211
// Object references
212
memoized({ id: 1 }, { id: 2 }); // Computed
213
memoized({ id: 1 }, { id: 2 }); // Cache miss (different object references)
214
```
215
216
## Error Handling
217
218
When the memoized function throws an error, memoize-one handles it with specific behavior:
219
220
1. **Exception Propagation**: If the memoized function throws, the memoized function also throws
221
2. **No Caching of Errors**: Thrown results are never cached - each call with arguments that cause an exception will re-execute the function
222
3. **Cache Preservation**: Previous successful cache results remain intact even when exceptions occur
223
224
**Examples:**
225
226
```typescript
227
import memoizeOne from "memoize-one";
228
229
const canThrow = (shouldThrow: boolean, value: number) => {
230
if (shouldThrow) {
231
throw new Error("Something went wrong");
232
}
233
return value * 2;
234
};
235
236
const memoized = memoizeOne(canThrow);
237
238
// Successful call - result cached
239
const result1 = memoized(false, 5); // Returns 10, cached
240
241
// Exception call - no caching
242
try {
243
memoized(true, 3); // Throws Error, not cached
244
} catch (e) {
245
console.log("First error:", e.message);
246
}
247
248
// Same exception call - function called again (no cache)
249
try {
250
memoized(true, 3); // Throws Error again, function re-executed
251
} catch (e) {
252
console.log("Second error:", e.message);
253
}
254
255
// Previous successful result still cached
256
const result2 = memoized(false, 5); // Returns 10 from cache (not re-executed)
257
console.log(result1 === result2); // true
258
```