memoize-one
Advanced tools
Comparing version 4.1.0 to 5.0.0
'use strict'; | ||
var simpleIsEqual = function simpleIsEqual(a, b) { | ||
return a === b; | ||
var shallowEqual = function shallowEqual(newValue, oldValue) { | ||
return newValue === oldValue; | ||
}; | ||
var simpleIsEqual = function simpleIsEqual(newArgs, lastArgs) { | ||
return newArgs.length === lastArgs.length && newArgs.every(function (newArg, index) { | ||
return shallowEqual(newArg, lastArgs[index]); | ||
}); | ||
}; | ||
function index (resultFn, isEqual) { | ||
@@ -17,6 +23,2 @@ if (isEqual === void 0) { | ||
var isNewArgEqualToLast = function isNewArgEqualToLast(newArg, index) { | ||
return isEqual(newArg, lastArgs[index], index); | ||
}; | ||
var result = function result() { | ||
@@ -27,3 +29,3 @@ for (var _len = arguments.length, newArgs = new Array(_len), _key = 0; _key < _len; _key++) { | ||
if (calledOnce && lastThis === this && newArgs.length === lastArgs.length && newArgs.every(isNewArgEqualToLast)) { | ||
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) { | ||
return lastResult; | ||
@@ -30,0 +32,0 @@ } |
@@ -1,5 +0,11 @@ | ||
var simpleIsEqual = function simpleIsEqual(a, b) { | ||
return a === b; | ||
var shallowEqual = function shallowEqual(newValue, oldValue) { | ||
return newValue === oldValue; | ||
}; | ||
var simpleIsEqual = function simpleIsEqual(newArgs, lastArgs) { | ||
return newArgs.length === lastArgs.length && newArgs.every(function (newArg, index) { | ||
return shallowEqual(newArg, lastArgs[index]); | ||
}); | ||
}; | ||
function index (resultFn, isEqual) { | ||
@@ -15,6 +21,2 @@ if (isEqual === void 0) { | ||
var isNewArgEqualToLast = function isNewArgEqualToLast(newArg, index) { | ||
return isEqual(newArg, lastArgs[index], index); | ||
}; | ||
var result = function result() { | ||
@@ -25,3 +27,3 @@ for (var _len = arguments.length, newArgs = new Array(_len), _key = 0; _key < _len; _key++) { | ||
if (calledOnce && lastThis === this && newArgs.length === lastArgs.length && newArgs.every(isNewArgEqualToLast)) { | ||
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) { | ||
return lastResult; | ||
@@ -28,0 +30,0 @@ } |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global.memoizeOne = factory()); | ||
}(this, (function () { 'use strict'; | ||
global.memoizeOne = factory(); | ||
}(typeof self !== 'undefined' ? self : this, function () { 'use strict'; | ||
var simpleIsEqual = function simpleIsEqual(a, b) { | ||
return a === b; | ||
var shallowEqual = function shallowEqual(newValue, oldValue) { | ||
return newValue === oldValue; | ||
}; | ||
var simpleIsEqual = function simpleIsEqual(newArgs, lastArgs) { | ||
return newArgs.length === lastArgs.length && newArgs.every(function (newArg, index) { | ||
return shallowEqual(newArg, lastArgs[index]); | ||
}); | ||
}; | ||
function index (resultFn, isEqual) { | ||
@@ -21,6 +27,2 @@ if (isEqual === void 0) { | ||
var isNewArgEqualToLast = function isNewArgEqualToLast(newArg, index) { | ||
return isEqual(newArg, lastArgs[index], index); | ||
}; | ||
var result = function result() { | ||
@@ -31,3 +33,3 @@ for (var _len = arguments.length, newArgs = new Array(_len), _key = 0; _key < _len; _key++) { | ||
if (calledOnce && lastThis === this && newArgs.length === lastArgs.length && newArgs.every(isNewArgEqualToLast)) { | ||
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) { | ||
return lastResult; | ||
@@ -48,2 +50,2 @@ } | ||
}))); | ||
})); |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.memoizeOne=n()}(this,function(){"use strict";var e=function(e,n){return e===n};return function(r,t){var i;void 0===t&&(t=e);var o,u=[],f=!1,c=function(e,n){return t(e,u[n],n)};return function(){for(var e=arguments.length,n=new Array(e),t=0;t<e;t++)n[t]=arguments[t];return f&&i===this&&n.length===u.length&&n.every(c)||(o=r.apply(this,n),f=!0,i=this,u=n),o}}}); | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.memoizeOne=n()}("undefined"!=typeof self?self:this,function(){"use strict";var e=function(e,f){return e.length===f.length&&e.every(function(e,n){return t=e,r=f[n],t===r;var t,r})};return function(r,f){var i;void 0===f&&(f=e);var o,u=[],d=!1;return function(){for(var e=arguments.length,n=new Array(e),t=0;t<e;t++)n[t]=arguments[t];return d&&i===this&&f(n,u)||(o=r.apply(this,n),d=!0,i=this,u=n),o}}}); |
{ | ||
"name": "memoize-one", | ||
"version": "4.1.0", | ||
"version": "5.0.0", | ||
"description": "A memoization library which only remembers the latest invocation", | ||
@@ -26,3 +26,3 @@ "main": "dist/memoize-one.cjs.js", | ||
"devDependencies": { | ||
"@babel/core": "^7.2.0", | ||
"@babel/core": "^7.2.2", | ||
"@babel/preset-env": "^7.2.0", | ||
@@ -35,9 +35,13 @@ "@babel/preset-flow": "^7.0.0", | ||
"eslint": "5.10.0", | ||
"eslint-config-prettier": "^3.3.0", | ||
"eslint-plugin-flowtype": "^3.2.0", | ||
"eslint-plugin-jest": "^22.1.2", | ||
"flow-bin": "0.88.0", | ||
"eslint-plugin-prettier": "^3.0.0", | ||
"flow-bin": "0.89.0", | ||
"jest": "^23.6.0", | ||
"lodash.isequal": "^4.5.0", | ||
"prettier": "1.15.3", | ||
"rimraf": "2.6.2", | ||
"rollup": "^0.67.4", | ||
"rollup-plugin-babel": "^4.0.3", | ||
"rollup": "^0.68.0", | ||
"rollup-plugin-babel": "^4.1.0", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
@@ -47,2 +51,5 @@ "rollup-plugin-replace": "^2.1.0", | ||
}, | ||
"config": { | ||
"prettier_target": "*.{js,jsx,md,json} src/**/*.{js,jsx,md,json} test/**/*.{js,jsx,md,json}" | ||
}, | ||
"scripts": { | ||
@@ -52,3 +59,6 @@ "validate": "yarn run lint && yarn run typecheck", | ||
"typecheck": "flow check", | ||
"lint": "eslint src test", | ||
"prettier:check": "yarn prettier --debug-check $npm_package_config_prettier_target", | ||
"prettier:write": "yarn prettier --write $npm_package_config_prettier_target", | ||
"lint:eslint": "eslint src test", | ||
"lint": "yarn lint:eslint && yarn prettier:check", | ||
"build": "yarn run build:clean && yarn run build:dist && yarn run build:flow", | ||
@@ -55,0 +65,0 @@ "build:clean": "rimraf dist", |
159
README.md
@@ -14,10 +14,2 @@ # memoize-one | ||
Cache invalidation is hard: | ||
> There are only two hard things in Computer Science: cache invalidation and naming things. | ||
> | ||
> *Phil Karlton* | ||
So keep things simple and just use a cache size of one! | ||
Unlike other memoization libraries, `memoize-one` only remembers the latest arguments and result. No need to worry about cache busting mechanisms such as `maxAge`, `maxSize`, `exclusions` and so on which can be prone to memory leaks. `memoize-one` simply remembers the last arguments, and if the function is next called with the same arguments then it returns the previous result. | ||
@@ -52,127 +44,81 @@ | ||
### Custom equality function | ||
## Installation | ||
You can also pass in a custom function for checking the equality of two items. The equality function will is used to compare the value of every individual argument. | ||
```bash | ||
# yarn | ||
yarn add memoize-one | ||
```js | ||
import memoizeOne from 'memoize-one'; | ||
import deepEqual from 'lodash.isEqual'; | ||
# npm | ||
npm install memoize-one --save | ||
``` | ||
const identity = x => x; | ||
## Module usage | ||
const defaultMemoization = memoizeOne(identity); | ||
const customMemoization = memoizeOne(identity, deepEqual); | ||
### ES6 module | ||
const result1 = defaultMemoization({foo: 'bar'}); | ||
const result2 = defaultMemoization({foo: 'bar'}); | ||
result1 === result2 // false - difference reference | ||
const result3 = customMemoization({foo: 'bar'}); | ||
const result4 = customMemoization({foo: 'bar'}); | ||
result3 === result4 // true - arguments are deep equal | ||
```js | ||
import memoizeOne from 'memoize-one'; | ||
``` | ||
#### Equality function type signature | ||
### CommonJS | ||
Here is the expected [flow](http://flowtype.org) type signature for a custom equality function: | ||
If you are in a CommonJS environment (eg [Node](https://nodejs.org)), then **you will need to add `.default` to your import**: | ||
```js | ||
type EqualityFn = (newValue: mixed, oldValue: mixed, index: number) => boolean; | ||
const memoizeOne = require('memoize-one').default; | ||
``` | ||
The default equality function is a simple shallow equal check | ||
## Custom equality function | ||
You can also pass in a custom function for checking the equality of two sets of arguments | ||
```js | ||
const simpleIsEqual: EqualityFn = (a: mixed, b: mixed): boolean => a === b; | ||
const memoized = memoizeOne(fn, isEqual); | ||
type EqualityFn = (newArgs: mixed[], oldArgs: mixed[]) => boolean; | ||
``` | ||
#### Equality function with multiple arguments | ||
An equality function should return `true` if the arguments are equal. If `true` is returned then the wrapped function will not be called. | ||
If the function you want to memoize takes multiple arguments, your custom equality function will be called once for each argument and will be passed each argument's new value and last value. | ||
The default equality function is a shallow equal check of all arguments (each argument is compared with `===`). The default equality function also does not check anything if the length of the arguments changes. You are welcome to decide if you want to return `false` if the `length` of the arguments is not equal | ||
```js | ||
import memoizeOne from 'memoize-one'; | ||
const simpleIsEqual: EqualityFn = ( | ||
newArgs: mixed[], | ||
lastArgs: mixed[], | ||
): boolean => | ||
newArgs.length === lastArgs.length && | ||
newArgs.every( | ||
(newArg: mixed, index: number): boolean => | ||
shallowEqual(newArg, lastArgs[index]), | ||
); | ||
``` | ||
const makeCountObj = (first, second, third) => ({ | ||
first: first.count, | ||
second: second.count, | ||
third: third.count, | ||
}); | ||
A custom equality function needs to compare `Arrays`. The `newArgs` array will be a new reference every time so a simple `newArgs === lastArgs` will always return `false`. | ||
const areCountPropertiesEqual = (newArg, lastArg) => newArg.count === lastArg.count; | ||
// runs once for first's new and last values, once for second's, etc. | ||
Equality functions are not called if the `this` context of the function has changed (see below). | ||
const memoizedMakeCountObj = memoizeOne(makeCountObj, areCountPropertiesEqual); | ||
Here is an example that uses a `lodash.isequal` deep equal equality check | ||
const result1 = memoizedMakeCountObj( | ||
{a: '?', count: 1}, | ||
{a: '$', count: 2}, | ||
{a: '#', count: 3} | ||
); | ||
const result2 = memoizedMakeCountObj( | ||
{b: null, count: 1}, | ||
{b: null, count: 2}, | ||
{b: null, count: 3} | ||
); | ||
> `lodash.isequal` correctly handles deep comparing two arrays | ||
result1 === result2; // true - same reference | ||
``` | ||
#### Equality function index | ||
For each call of the equality function you are provided with the index of the argument. | ||
```js | ||
import memoizeOne from 'memoize-one'; | ||
import deepEqual from 'lodash.isEqual'; | ||
import isDeepEqual 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 identity = x => x; | ||
const fn = (...args) => { | ||
console.log('called with', ...args); | ||
}; | ||
const memoized = memoizeOne(fn, myEqualFn); | ||
const shallowMemoized = memoizeOne(identity); | ||
const deepMemoized = memoizeOne(identity, isDeepEqual); | ||
memoized({hello: 'world'}, 5); | ||
// console.log('called with', {hello: 'world'}, 5); | ||
const result1 = shallowMemoized({ foo: 'bar' }); | ||
const result2 = shallowMemoized({ foo: 'bar' }); | ||
memoized({hello: 'world'}, 5); | ||
// no call to console.log | ||
``` | ||
result1 === result2; // false - difference reference | ||
## Installation | ||
const result3 = deepMemoized({ foo: 'bar' }); | ||
const result4 = deepMemoized({ foo: 'bar' }); | ||
```bash | ||
# yarn | ||
yarn add memoize-one | ||
# npm | ||
npm install memoize-one --save | ||
result3 === result4; // true - arguments are deep equal | ||
``` | ||
## Module usage | ||
### ES6 module | ||
```js | ||
import memoizeOne from 'memoize-one'; | ||
``` | ||
### CommonJS | ||
If you are in a CommonJS environment (eg [Node](https://nodejs.org)), then **you will need to add `.default` to your import**: | ||
```js | ||
const memoizeOne = require('memoize-one').default; | ||
``` | ||
## `this` | ||
@@ -205,3 +151,3 @@ | ||
a: 30, | ||
} | ||
}; | ||
@@ -214,3 +160,3 @@ getA.call(temp1); // 20 | ||
Generally this will be of no impact if you are not explicity controlling the `this` context of functions you want to memoize with [explicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#explicit-binding) or [implicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#implicit-binding). `memoize-One` will detect when you are manipulating `this` and will then consider the `this` context as an argument. If `this` changes, it will re-execute the original function even if the arguments have not changed. | ||
Generally this will be of no impact if you are not explicity controlling the `this` context of functions you want to memoize with [explicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#explicit-binding) or [implicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#implicit-binding). `memoize-One` will detect when you are manipulating `this` and will then consider the `this` context as an argument. If `this` changes, it will re-execute the original function even if the arguments have not changed. | ||
@@ -226,7 +172,7 @@ ## When your result function `throw`s | ||
console.log('called'); | ||
if(name === 'throw') { | ||
if (name === 'throw') { | ||
throw new Error(name); | ||
} | ||
return { name }; | ||
} | ||
}; | ||
@@ -246,3 +192,3 @@ const memoized = memoizeOne(canThrow); | ||
// console.log => 'called' | ||
} catch(e) { | ||
} catch (e) { | ||
firstError = e; | ||
@@ -256,3 +202,3 @@ } | ||
// with the 'throw' string | ||
} catch(e) { | ||
} catch (e) { | ||
secondError = e; | ||
@@ -263,3 +209,2 @@ } | ||
const value3 = memoized('Alex'); | ||
@@ -286,3 +231,3 @@ // result function not called as the original memoization cache has not been busted | ||
The comparisions are not exhaustive and are primiarly to show that `memoize-one` accomplishes remembering the latest invocation really fast. The benchmarks do not take into account the differences in feature sets, library sizes, parse time, and so on. | ||
The comparisons are not exhaustive and are primarily to show that `memoize-one` accomplishes remembering the latest invocation really fast. The benchmarks do not take into account the differences in feature sets, library sizes, parse time, and so on. | ||
@@ -289,0 +234,0 @@ ## Code health :thumbsup: |
// @flow | ||
type EqualityFn = (newValue: mixed, oldValue: mixed, index: number) => boolean; | ||
export type EqualityFn = (newArgs: mixed[], lastArgs: mixed[]) => boolean; | ||
const simpleIsEqual: EqualityFn = (a: mixed, b: mixed): boolean => a === b; | ||
const shallowEqual = (newValue: mixed, oldValue: mixed): boolean => | ||
newValue === oldValue; | ||
const simpleIsEqual: EqualityFn = ( | ||
newArgs: mixed[], | ||
lastArgs: mixed[], | ||
): boolean => | ||
newArgs.length === lastArgs.length && | ||
newArgs.every( | ||
(newArg: mixed, index: number): boolean => | ||
shallowEqual(newArg, lastArgs[index]), | ||
); | ||
// <ResultFn: (...Array<any>) => mixed> | ||
@@ -12,3 +23,6 @@ // The purpose of this typing is to ensure that the returned memoized | ||
// mixed: The result can be anything but needs to be checked before usage | ||
export default function <ResultFn: (...Array<any>) => mixed>(resultFn: ResultFn, isEqual?: EqualityFn = simpleIsEqual): ResultFn { | ||
export default function<ResultFn: (...Array<any>) => mixed>( | ||
resultFn: ResultFn, | ||
isEqual?: EqualityFn = simpleIsEqual, | ||
): ResultFn { | ||
let lastThis: mixed; | ||
@@ -19,10 +33,5 @@ let lastArgs: Array<mixed> = []; | ||
const isNewArgEqualToLast = (newArg: mixed, index: number): boolean => isEqual(newArg, lastArgs[index], index); | ||
// breaking cache when context (this) or arguments change | ||
const result = function (...newArgs: Array<mixed>) { | ||
if (calledOnce && | ||
lastThis === this && | ||
newArgs.length === lastArgs.length && | ||
newArgs.every(isNewArgEqualToLast)) { | ||
const result = function(...newArgs: Array<mixed>) { | ||
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) { | ||
return lastResult; | ||
@@ -43,2 +52,2 @@ } | ||
return (result: any); | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
150
0
17530
22
233