Releases: alexreardon/memoize-one
6.0.0
At a glance
🧹 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-one
will soon™️ be dropping ie11 support
More details
New: Cache releasing with .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:
- Release memory
- Allow the underlying function to be called again without having to change arguments
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);
Improved: memoized function type
There 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
type
change has been labelled as apatch
(fix) as the previous type was not correct. However, you could consider it amajor
given that the new type is narrower than before
Improved: equality function type
There 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
TypeScript
types to start failing, so this change has been listed as amajor
Improved: Documentation
- Cleanup and audit of examples
- Added documentation for
.clear()
- Added documentation about function properties and the
.length
property - Updated performance benchmarks
Internal code health
- Now on
[email protected]
- Moved from TravisCI to Github workflows
- Upgraded
devDependencies
Thanks ❤️
Thanks so much to the following people who helped make this release possible:
- @danieldelcore
- @oriSomething
- @TrySound
- @theKashey
- @AndrewOCC
- @wbinnssmith
- @bolasblack
- @OliverJAsh
- @Offirmo
Catch you next time,
5.2.1
5.2.0
⚠️ 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';
New feature: Added a named import #37
DEPRECATED Please continue to use default import
This resulted in a
minor
bump 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';
Fix: Correctly handling NaN
#101
Our 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
Improvement: Documentation
I have added more detail to the readme which explains in greater detail how our default equality function works
Improvement: Bumping dev dependencies
I have upgraded all the devDependencies
of memoize-one
to be their latest versions. A reminder that memoize-one
has no dependencies
🎉
5.1.1
Looser EqualityFn
type
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 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
5.1.0
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
flow
consumers
❤ 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
5.0.5
5.0.4
5.0.3
⚠️ 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
🚀 Performance #59
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!
👩⚕️ Improved typing #61
Removed in 5.0.4
Thanks to @wbinnssmith we now have even better flow
typing.
Other
- Upgrading to
flow
0.96
#63