NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1414 most downloaded on npm
A memoization library which only remembers the latest invocation
Last release 5 years ago
no release in 18 months
Release timing varies
gaps range from 2 weeks to 1.7 years
Most releases are documented
notes for 16 of 21 stable releases
4 versions withdrawn
withdrawn after publishing
10 years old
35 releases · first in 2017
🧹 New .clear() function to remove the current memoization cache 🏋️♀️ Stronger types 🥰 Improved documentation
🧹 New .clear() function to remove the current memoization cache
🏋️♀️ Stronger types
🥰 Improved documentation
This release is a major, but there are no behaviour or API changes. The major is to reflect that some of the TypeScript types have been tightened which might cause some peoples TypeScript builds to break.
PSA:
memoize-onewill soon™️ be dropping ie11 support More details
.clear() 🧹A .clear() property is now added to memoized functions to allow you to clear it's memoization cache
This is helpful if you want to:
import memoizeOne from 'memoize-one';
function add(a: number, b: number): number {
return a + b;
}
const memoizedAdd = memoizeOne(add);
// first call - not memoized
const first = memoizedAdd(1, 2);
// second call - cache hit (underlying function not called)
const second = memoizedAdd(1, 2);
// 👋 clearing memoization cache
memoizedAdd.clear();
// third call - not memoized (cache was cleared)
const third = memoizedAdd(1, 2);
typeThere are no API changes to
memoize-one, this is merely a typing improvement
Our previous type for a memoized function was simple:
Previous
export declare type EqualityFn = (newArgs: any[], lastArgs: any[]) => boolean;
declare function memoizeOne<ResultFn extends (this: any, ...newArgs: any[]) => ReturnType<ResultFn>>(resultFn: ResultFn, isEqual?: EqualityFn): ResultFn;
A memoized function claimed to be the same type (ResultFn) as the original function. This was not true, as memoize-one does not copy of any existing object properties on the original function (TFunc)
Updated
export declare type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
clear: () => void;
(this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
};
declare function memoizeOne<TFunc extends (this: any, ...newArgs: any[]) => any>(
resultFn: TFunc,
isEqual?: EqualityFn<TFunc>,
): MemoizedFn<TFunc>;
A memoized function returns the same callable signature as the original function (TFunc → was ResultFn), but it makes it clear that no function object properties on the original function (TFunc) are being carried forward. The memoized function also now includes a .clear() function object property
If you want to continue to use the old types where the memoized function is the same type as the function being memoized, you can achieve this by casting the type of your memoized function:
function add(first: number, second: number): number {
return first + second;
}
// a type that matches our add function
type AddFn = (first: number, second: number) => number;
// type of memoized will be MemoizedFn<typeof add>
const memoized = memoize(add);
// option 1
const memoized: typeof add = memoize(add);
// option 2
const memoized: AddFn = memoize(add);
// option 3
const memoized = memoize(add) as typeof add;
// option 4
const memoized = memoize(add) as AddFn;
This
typechange has been labelled as apatch(fix) as the previous type was not correct. However, you could consider it amajorgiven that the new type is narrower than before
typeThere are no API changes to equality functions, this is merely a typing improvement
Previous
export type EqualityFn = (newArgs: any[], lastArgs: any[]) => boolean;
Current
export type EqualityFn<TFunc extends (...args: any[]) => any> = (
newArgs: Parameters<TFunc>,
lastArgs: Parameters<TFunc>,
) => boolean;
This looks a little scary, but it is pretty neat! It means that you can dramatically improve the type safety of your custom equality functions if you want to.
If you are not using a custom equality function
No changes for you!
If you are using a custom equality function
Most people will not be impacted!
This type tightening allows you to be a lot stricter with the shape of your functions passed in as equality functions. If you are using generic equality functions such as lodash.isequal their types are loose and there is nothing you will need to do. But if you want to write more efficient and typesafe equality functions, you are in for a treat.
An example of what things looked like in 5.x
import memoize, { EqualityFn } from "memoize-one";
type Person = {
id: string;
name: string;
};
function invite(person: Person) {
// This could do something fancy, but just keeping things basic
console.log("invited:", person.name);
}
// Yuck, we don't know anything about the args
// Note: don't really need the `EqualityFn` type as it is just:
// `type EqualityFn = (newArgs: any[], lastArgs: any[]) => boolean;`
const isEqual: EqualityFn = (newArgs: any[], lastArgs: any[]): boolean => {
// Yuck #2: we have to cast here
// We would also be creating a bug if isEqual is used on a function
// that has no arguments
const first = newArgs[0] as Person;
const second = lastArgs[0] as Person;
return first.id === second.id;
};
const memoized = memoize(invite, isEqual);
const alex: Person = {
name: "Alex",
id: "11111"
};
memoized(alex);
// Won't do anything as `alex` has the same id as the last `alex`
memoized(alex);
→ You can play with this example on codesandbox.io
The same example in 6.x
import memoize, { EqualityFn } from "memoize-one";
type Person = {
id: string;
name: string;
};
function invite(person: Person) {
console.log("invited:", person.name);
}
// Yum: we know that newArgs + lastArgs are the tuple `[Person]`
const isEqual: EqualityFn<typeof invite> = ([first], [second]): boolean => {
return first.id === second.id;
};
const memoized = memoize(invite, isEqual);
const alex: Person = {
name: "Alex",
id: "11111"
};
memoized(alex);
// Won't do anything as `alex` has the same id as the last `alex`
memoized(alex);
// When declared inline, our isEqual function has the correct types inferred
const inferred = memoize(invite, function isEqual([first], [second]): boolean {
return first.id === second.id;
});
→ You can play with this example on codesandbox.io
There are a few cases where this could cause your
TypeScripttypes to start failing, so this change has been listed as amajor
.clear().length propertyTypeScript@4.4.3devDependenciesThanks so much to the following people who helped make this release possible:
Catch you next time,
One column per quarter.
Nothing published for this version
The addition of a named import for memoize-one in 5.2.0 created an unintentional breaking change for our CommonJS bundle #116 (Thanks @ehmicky for fin…
The addition of a named import for memoize-one in 5.2.0 created an unintentional breaking change for our CommonJS bundle #116 (Thanks @ehmicky for finding this)
5.2.1 reverts the addition of the named import of 5.2.0. 5.2.0 has also been deprecated on npm
The addition of our named import created a breaking change for our CommonJS build #116 (Thanks @ehmicky for finding this)
5.2.0 is deprecated on npm ⚠️The addition of our named import created a breaking change for our CommonJS build #116 (Thanks @ehmicky for finding this)
The named import feature has been reverted and you can continue to use the default import has you always have
import memoizeOne from 'memoize-one';
DEPRECATED Please continue to use default import
This resulted in a
minorbump for the library
You can now import memoize-one using a named import if you want
import { memoizeOne } from 'memoize-one';
Alternatively, you can continue to use the default import
import memoizeOne from 'memoize-one';
NaN #101Our default equality checking function does a === equality check for all arguments. This was problematic when providing special "not a number" number → NaN as NaN !== NaN. Our default equality function now handles NaN values correctly
Thank you @ohoho7 for raising this and @Ayub-Begimkulov for diving it forward
I have added more detail to the readme which explains in greater detail how our default equality function works
I have upgraded all the devDependencies of memoize-one to be their latest versions. A reminder that memoize-one has no dependencies 🎉
For 5.1.0 we shipped an EqualityFn type that was not ideal. It was decided that the simplest path forward for consumers was to move to a looser Equali
EqualityFn typeFor 5.1.0 we shipped an EqualityFn type that was not ideal. It was decided that the simplest path forward for consumers was to move to a looser EqualityFn type. #73
- export type EqualityFn = (newArgs: readonly unknown[], lastArgs: readonly unknown[]) => boolean;
+ export type EqualityFn = (newArgs: any[], lastArgs: any[]) => boolean;
Thanks @SanderDeWaal1992 for raising this issue
EqualityFn typeFor 5.1.0 we shipped an EqualityFn type that was not ideal. It was decided that the simplest path forward for consumers was to move to a looser EqualityFn type. #73
- export type EqualityFn = (newArgs: readonly unknown[], lastArgs: readonly unknown[]) => boolean;
+ export type EqualityFn = (newArgs: any[], lastArgs: any[]) => boolean;Thanks @SanderDeWaal1992 for raising this issue
Typescript consumers will now start getting correct types without having to rely on installing @types/memoize-one. Internally memoize-one is now autho
Typescript support! 🤘🤩🤘Typescript consumers will now start getting correct types without having to rely on installing @types/memoize-one. Internally memoize-one is now authored in Typescript.
memoize-one is still shipping flow types, so if you are using, or want to use flow, then you will still get the same fantastic types you always have.
And if you want to use good old regular vanilla JS then you can do that too!
This change has been marked as a feature release as it will not break any existing
flowconsumers
❤ Thanks to @PavelVanecekAtlassian, @danieldelcore and @TrySound for their assistance. And thanks to @karol-majewski and @franklixuefei for creating the previous memoize-one typescript types on DefinitelyTyped
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Naming functions for better stack traces #68
travis build and adding .nvm file #69This change broke existing flow type consumers. We need to look into how our local flow type tests did not pick this up. For now, the flow change has
This change broke existing flow type consumers. We need to look into how our local flow type tests did not pick this up. For now, the flow change has been reverted. If we do move to the new flow types then we will need to publish a good upgrade story.
> ⚠️ This release has been deprecated on npm. However, all improvements are available in 5.0.4. There were some issues with the updated flow types
⚠️ This release has been deprecated on npm. However, all improvements are available in 5.0.4. There were some issues with the updated flow types
We tweaked our default equality function to run much faster.
From 1.1 to 15.5 times faster!
| Target | Change |
|---|---|
| Node 8.11.3 | 1450% faster (8 million ops/sec => 124 million ops/sec) |
| Node 11.12 | 23% faster |
| Chrome (latest) | 10% faster |
| Safari (latest) | 50% faster |
| Firefox (latest) | 10% faster |
Thanks to @wbinnssmith for creating performance benchmarks to test ideas #62. Thanks @theKashey for your input!
Removed in 5.0.4
Thanks to @wbinnssmith we now have even better flow typing.
flow 0.96 #63Fixing flow issue #58. Caused by using non-standard $ExpectError comment. Thanks @jmansor for the pick up
flow issue #58. Caused by using non-standard $ExpectError comment. Thanks @jmansor for the pick upBumping all dev dependencies #56
0.95.1 #56> This resulted in a breaking change 💥. However, it will only impact you if you are using a custom equality function. Addtionally, some libraries such…
Previously we could call a custom equality function for each argument. Now we call it once and pass in the complete sets of arguments. This allows for a lot more flexibility.
- type EqualityFn = (newValue: mixed, oldValue: mixed) => boolean;
+ type EqualityFn = (newArgs: mixed[], lastArgs: mixed[]) => boolean
const memoized = memoizeOne(fn, isEqual);
memoized(1, 2);
// isEqual not called on first call
memoized(1, 3);
// isEqual called two times
// first call: (1, 1) (newArg, oldArg)
// second call: (3, 2) (newArg, oldArg)
const memoized = memoizeOne(fn, isEqual);
memoized(1, 2);
// isEqual not called on first call
memoized(1, 3);
// isEqual called one time
// first call: ([1, 3], [1, 2]) (newArgs, oldArgs)
We have provided a detailed usage guide
This resulted in a breaking change 💥. However, it will only impact you if you are using a custom equality function. Addtionally, some libraries such as
lodash.isequalwill handle this api change without you requiring to make any code changes.
flow 0.89prettier for formattingThanks to the following people for their assistance with this release ❤️:
> 🛑This release has been deprecated
🛑This release has been deprecated
index provided to equality function #42🛑This feature has been removed and replaced with a new custom equality pattern
Thanks @nihgwu for submitting this one!
If you are not providing your own custom equality function then there nothing to see here 👍
Custom equality functions are now provided with a third argument: the index of the argument.
-type EqualityFn = (newValue: mixed, oldValue: mixed) => boolean;
+type EqualityFn = (newValue: mixed, oldValue: mixed, index: number) => boolean;
This can be useful if you want to do different types of checking depending on the order of the argument.
import memoizeOne from 'memoize-one';
import deepEqual from 'lodash.isEqual';
const myEqualFn = (newArg, lastArg, index) => {
// use deep equal for first arg
if(index === 0) {
return deepEqual(newArg, lastArg);
}
// use shallow equal for all other arguments
return newArg === lastArg;
}
const fn = (...args) => {
console.log('called with', ...args);
};
const memoized = memoizeOne(fn, myEqualFn);
memoized({hello: 'world'}, 5);
// console.log('called with', {hello: 'world'}, 5);
memoized({hello: 'world'}, 5);
// no call to console.log
This resulted in a feature release
flow 0.88flow-typed lib definitions- Upgrading to flow 0.85 - Upgrading dev deps
flow 0.85We are still gracefully handling result functions that throw. We have made a few improvements:
throwWe are still gracefully handling result functions that throw. We have made a few improvements:
try / catch for maximum performance - more detailsthrow will no longer break the memoization cacheThanks @ChristopherChudzicki for originally pointing me in this direction
Previously if your result function threw a value you could get some strange behaviour. We have fixed this so that memoizeOne is aware when your result
throw #31Previously if your result function threw a value you could get some strange behaviour. We have fixed this so that memoizeOne is aware when your result function throws a value. If your result function throws then the memoized function will also throw. A thrown value is not cached and the result function will be re-executed if called again with the same arguments.
const willThrow = (message) => {
console.log(message);
throw new Error(message);
}
const memoized = memoizeOne(willThrow);
let firstError;
let secondError;
try {
memoized('first message');
// console.log => 'first message'
} catch (e) {
firstError = e;
}
try {
memoized('first message');
// console.log => 'first message'
// even though the arguments are the same the result function was called again
} catch (e) {
secondError = e;
}
// result is regenerated and not cached
console.log(firstError === secondError);
// false
Thanks @ChristopherChudzicki for raising this issue and for the initial PR. Thanks to @scinos and @jamiebuilds for working this through with me.
babel 7 #34flow 0.79.1 #34README #29. Thanks @andwilley!!> This was listed as a breaking change 💥 - even though there are no api changes. The only changes are to the names and paths of the build files.
We are now publishing a number of bundle builds for your consumption needs!
dist/memoize-one.cjs.js CommonJS bundledist/memoize-one.esm.js ESM bundledist/memoize-one.min.js UMD bundle (production build)dist/memoize-one.js UMD bundle (development build)This was listed as a breaking change 💥 - even though there are no api changes. The only changes are to the names and paths of the build files.
flowWe are now on the latest version of flow: 0.75.
For now we have rolled back the feature and deprecated 3.1.0 which added it. We could have wrapped the code in a try/catch but we where not sure of th…
.length property of our result function (added in 3.1.0). For now we have rolled back the feature and deprecated 3.1.0 which added it. We could have wrapped the code in a try/catch but we where not sure of the performance implications of going so. Thanks @eugene1g for finding this and @theKashey for helping work through it> This release has been deprecated on npm due to a critical bug in IE11. Please use 3.1.1.
This release has been deprecated on npm due to a critical bug in IE11. Please use
3.1.1.
.length property to the result function. Thanks @theKashey for this one!.name to the result function for improved debugging. Thanks @theKashey!flowtypemocha to jestNothing published for this version
As with 2.0.0 this release has no javascript api changes. This version bump is the result of this pull request: #12
As with 2.0.0 this release has no javascript api changes. This version bump is the result of this pull request: #12
In this pull request the flow types for memoize-one where improved to be more correct. In doing so the flow type became a little tighter and more correct. There may be a situation where some code could potentially be relying on the less stringent type checking and in which case this change would break their code. As this is a change in public api that may possibility by some remote chance break a consumer - I thought it safest to do a major version bump.
Nothing published for this version
I have created a github release for this major version bump because I thought it warranted an explanation.
I have created a github release for this major version bump because I thought it warranted an explanation.
This release has no javascript api changes so you can probably upgrade without any issues. The version bump is because of this pull request: #9.
This major version bump was the result of removing module from package.json:
- "module": "src/index.js",
module is used to expose raw es6 sources for bundlers. It seems like the main use case for using module is to allow removing duplicate imports via tree shaking. A few problems with module in memoize-one:
My thoughts on what would be the ideal module:
compiled es5 source with es6 imports.
Given that memoize-one has no dependencies there is no tree shaking advances in exposing it through module.
Given that some people may have been explicitly relying on using the module in memoize-one previously I have made it a major version bump to remove it. While I expect that most module bundlers would have simply switched to main with no issues - I wanted to be really safe and just do a major version bump. I think that module is a public api and removing it therefore constitutes a break in api.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →