use-memo-map 0.0.0-main.18b69b6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/lib/commonjs/index.js +15 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/private/usePrevious.js +16 -0
- package/lib/commonjs/private/usePrevious.js.map +1 -0
- package/lib/commonjs/private/useValueRef.js +25 -0
- package/lib/commonjs/private/useValueRef.js.map +1 -0
- package/lib/commonjs/useMemoMap.js +64 -0
- package/lib/commonjs/useMemoMap.js.map +1 -0
- package/lib/esmodules/index.js +3 -0
- package/lib/esmodules/index.js.map +1 -0
- package/lib/esmodules/private/usePrevious.js +9 -0
- package/lib/esmodules/private/usePrevious.js.map +1 -0
- package/lib/esmodules/private/useValueRef.js +17 -0
- package/lib/esmodules/private/useValueRef.js.map +1 -0
- package/lib/esmodules/useMemoMap.js +56 -0
- package/lib/esmodules/useMemoMap.js.map +1 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/private/usePrevious.d.ts +1 -0
- package/lib/types/private/useValueRef.d.ts +4 -0
- package/lib/types/useMemoMap.d.ts +15 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 William Wong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# `use-memo-map`
|
|
2
|
+
|
|
3
|
+
Memoizes calls to array map function similar to `React.useMemo`. Memoized results will survive next render.
|
|
4
|
+
|
|
5
|
+
## Background
|
|
6
|
+
|
|
7
|
+
tl;dr, `React.useMemo()` cache a single call. `useMemoMap()` cache multiple calls.
|
|
8
|
+
|
|
9
|
+
If you have a variable-length array and would like to cache `Array.map` like `useMemo`, you can use `useMemoMap` to cache all calls.
|
|
10
|
+
|
|
11
|
+
## How to use
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { useMemoMap } from 'use-memo-map';
|
|
15
|
+
|
|
16
|
+
const MyComponent = () => {
|
|
17
|
+
const multiplyBy10 = useCallback(value => {
|
|
18
|
+
// Calls to this function will be memoized based on its first argument.
|
|
19
|
+
// You can do expensive calls here.
|
|
20
|
+
return value * 10;
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
// useMemoMap() will return a function that take an array.
|
|
24
|
+
const map = useMemoMap(multiplyBy10);
|
|
25
|
+
|
|
26
|
+
const output = map([1, 2, 3]); // Returns [10, 20, 30].
|
|
27
|
+
|
|
28
|
+
return ...;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default MyComponent;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
type UseMemoMapOptions<T> = {
|
|
38
|
+
itemEquality?: (this: readonly T[], x: T, y: T) => boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function useMemoMap<T = unknown, R = unknown>(
|
|
42
|
+
mapper: (this: readonly T[], item: T, index: -1, array: readonly T[]) => R,
|
|
43
|
+
{ itemEquality = Object.is }: UseMemoMapOptions<T> = {}
|
|
44
|
+
): (array: readonly T[]) => readonly R[] {}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For example, converting a `number` array into a `string` array.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
function useMemoMap(
|
|
51
|
+
mapper: (item: number, index: -1, array: readonly number[]) => string
|
|
52
|
+
): (array: readonly number[]) => readonly string[];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Behaviors
|
|
56
|
+
|
|
57
|
+
### Invalidate all results when mapper function change
|
|
58
|
+
|
|
59
|
+
If the first argument (a.k.a. the mapper function) changed, it will invalidate all results. This is by design.
|
|
60
|
+
|
|
61
|
+
You should always use `useCallback` or `useMemo` to cache the mapper function.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const MyComponent = () => {
|
|
65
|
+
// ❌ In the next line, multiplyBy10() is a new instance on every render of <MyComponent>.
|
|
66
|
+
const multiplyBy10 = value => value * 10;
|
|
67
|
+
// ✔️ You should cache multiplyBy10() by React.useCallback().
|
|
68
|
+
const multiplyBy10 = useCallback(value => value * 10, []);
|
|
69
|
+
|
|
70
|
+
const map = useMemoMap(multiplyBy10);
|
|
71
|
+
|
|
72
|
+
// Calls to map() will not survive across render calls if multiplyBy10() changed.
|
|
73
|
+
const output = map([1, 2, 3]);
|
|
74
|
+
|
|
75
|
+
// ...
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Single cache pool for multiple calls
|
|
80
|
+
|
|
81
|
+
In a single render loop, if you call the returned function (a.k.a. the `map` function) multiple times, all calls will be sharing the same "cache pool".
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const MyComponent = () => {
|
|
85
|
+
// ...
|
|
86
|
+
|
|
87
|
+
const output1 = map([1, 2, 3]); // Return [10, 20, 30].
|
|
88
|
+
const output2 = map([1, 2, 3]); // Return [10, 20, 30] without calling `multiplyBy10`.
|
|
89
|
+
|
|
90
|
+
// ...
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Note: the "cache pool" will be saved during `useEffect()` callback. This is similar to `usePrevious()` hook.
|
|
95
|
+
|
|
96
|
+
### Index is always `-1`
|
|
97
|
+
|
|
98
|
+
Unlike `Array.map()` which pass the `index` value as second argument, `useMemoMap()` will always pass `-1`. This is by design.
|
|
99
|
+
|
|
100
|
+
Calls to mapper function could be cached and will not be called again if only `index` has changed. To protect wrong use of `index` value, we pass `-1` instead.
|
|
101
|
+
|
|
102
|
+
If you understand this risk, call `this.indexOf(item)` to get the `index` value.
|
|
103
|
+
|
|
104
|
+
### Returns read-only result
|
|
105
|
+
|
|
106
|
+
React loves immutability. By default, the array we returned is frozen (a.k.a. read-only).
|
|
107
|
+
|
|
108
|
+
If you prefer mutable array, call `[...result]` to clone the array.
|
|
109
|
+
|
|
110
|
+
### Custom equality function
|
|
111
|
+
|
|
112
|
+
We use `Object.is` for equality check. You can provide a different equality check via `options.itemEquality`.
|
|
113
|
+
|
|
114
|
+
## Contributions
|
|
115
|
+
|
|
116
|
+
Like us? [Star](https://github.com/compulim/use-memo-map/stargazers) us.
|
|
117
|
+
|
|
118
|
+
Want to make it better? [File](https://github.com/compulim/use-memo-map/issues) us an issue.
|
|
119
|
+
|
|
120
|
+
Don't like something you see? [Submit](https://github.com/compulim/use-memo-map/pulls) a pull request.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
5
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
_Object$defineProperty(exports, "useMemoMap", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function get() {
|
|
11
|
+
return _useMemoMap.default;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
var _useMemoMap = _interopRequireDefault(require("./useMemoMap"));
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import useMemoMap from './useMemoMap';\n\nexport { useMemoMap };\n"],"mappings":";;;;;;;;;;;;;AAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = usePrevious;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
function usePrevious(value) {
|
|
10
|
+
var ref = (0, _react.useRef)();
|
|
11
|
+
(0, _react.useEffect)(function () {
|
|
12
|
+
ref.current = value;
|
|
13
|
+
});
|
|
14
|
+
return ref.current;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=usePrevious.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrevious.js","names":["usePrevious","value","ref","useRef","useEffect","current"],"sources":["../../../src/private/usePrevious.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport default function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T>();\n\n useEffect(() => {\n ref.current = value;\n });\n\n return ref.current;\n}\n"],"mappings":";;;;;;;AAAA;AAEe,SAASA,WAAW,CAAIC,KAAQ,EAAiB;EAC9D,IAAMC,GAAG,GAAG,IAAAC,aAAM,GAAK;EAEvB,IAAAC,gBAAS,EAAC,YAAM;IACdF,GAAG,CAACG,OAAO,GAAGJ,KAAK;EACrB,CAAC,CAAC;EAEF,OAAOC,GAAG,CAACG,OAAO;AACpB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
5
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = useValueRef;
|
|
9
|
+
var _create = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/create"));
|
|
10
|
+
var _react = require("react");
|
|
11
|
+
function useValueRef(value) {
|
|
12
|
+
var ref = (0, _react.useRef)();
|
|
13
|
+
var readOnlyRef = (0, _react.useMemo)(function () {
|
|
14
|
+
return (0, _create.default)({}, {
|
|
15
|
+
current: {
|
|
16
|
+
get: function get() {
|
|
17
|
+
return ref.current;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}, [ref]);
|
|
22
|
+
ref.current = value;
|
|
23
|
+
return readOnlyRef;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=useValueRef.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useValueRef.js","names":["useValueRef","value","ref","useRef","readOnlyRef","useMemo","current","get"],"sources":["../../../src/private/useValueRef.ts"],"sourcesContent":["import { useMemo, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nexport default function useValueRef<T>(value: T): RefObject<T> & { current: T } {\n const ref = useRef<T>();\n const readOnlyRef = useMemo(() => Object.create({}, { current: { get: () => ref.current } }), [ref]);\n\n ref.current = value;\n\n return readOnlyRef;\n}\n"],"mappings":";;;;;;;;;AAAA;AAGe,SAASA,WAAW,CAAIC,KAAQ,EAAiC;EAC9E,IAAMC,GAAG,GAAG,IAAAC,aAAM,GAAK;EACvB,IAAMC,WAAW,GAAG,IAAAC,cAAO,EAAC;IAAA,OAAM,qBAAc,CAAC,CAAC,EAAE;MAAEC,OAAO,EAAE;QAAEC,GAAG,EAAE;UAAA,OAAML,GAAG,CAACI,OAAO;QAAA;MAAC;IAAE,CAAC,CAAC;EAAA,GAAE,CAACJ,GAAG,CAAC,CAAC;EAEpGA,GAAG,CAACI,OAAO,GAAGL,KAAK;EAEnB,OAAOG,WAAW;AACpB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
5
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = useMemoMap;
|
|
9
|
+
var _is = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/is"));
|
|
10
|
+
var _freeze = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/freeze"));
|
|
11
|
+
var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));
|
|
12
|
+
var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
|
|
13
|
+
var _react = require("react");
|
|
14
|
+
var _usePrevious = _interopRequireDefault(require("./private/usePrevious"));
|
|
15
|
+
var _useValueRef = _interopRequireDefault(require("./private/useValueRef"));
|
|
16
|
+
/**
|
|
17
|
+
* Creates a memoized mapping function.
|
|
18
|
+
*
|
|
19
|
+
* Unlike `React.useMemo`, the mapping function can be called multiple times in a single render loop.
|
|
20
|
+
* All calls to the mapping function will be memoized.
|
|
21
|
+
*
|
|
22
|
+
* The memoized arguments and return values will survive next render.
|
|
23
|
+
*
|
|
24
|
+
* When the mapping function change, all memoized values will be invalidated.
|
|
25
|
+
*/
|
|
26
|
+
function useMemoMap(mapper) {
|
|
27
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
28
|
+
_ref$itemEquality = _ref.itemEquality,
|
|
29
|
+
itemEquality = _ref$itemEquality === void 0 ? _is.default : _ref$itemEquality;
|
|
30
|
+
var itemEqualityRef = (0, _useValueRef.default)(itemEquality);
|
|
31
|
+
var lastCallsRef = (0, _react.useRef)([]);
|
|
32
|
+
var mapperRef = (0, _useValueRef.default)(mapper);
|
|
33
|
+
var thisCalls = [];
|
|
34
|
+
if ((0, _usePrevious.default)(mapper) !== mapper) {
|
|
35
|
+
lastCallsRef.current = [];
|
|
36
|
+
}
|
|
37
|
+
(0, _react.useEffect)(function () {
|
|
38
|
+
lastCallsRef.current = (0, _freeze.default)(thisCalls);
|
|
39
|
+
});
|
|
40
|
+
var thisCallsRef = (0, _useValueRef.default)(thisCalls);
|
|
41
|
+
return (0, _react.useCallback)(function (array) {
|
|
42
|
+
var itemEquality = itemEqualityRef.current;
|
|
43
|
+
var mapper = mapperRef.current;
|
|
44
|
+
var thisCalls = thisCallsRef.current;
|
|
45
|
+
return (0, _freeze.default)((0, _map.default)(array).call(array, function (item) {
|
|
46
|
+
var _context;
|
|
47
|
+
var thisCall = (0, _find.default)(thisCalls).call(thisCalls, function (entry) {
|
|
48
|
+
return itemEquality.call(array, item, entry[0]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// If this call has memoized in the current render loop, use the memoized return value.
|
|
52
|
+
if (thisCall) {
|
|
53
|
+
return thisCall[1];
|
|
54
|
+
}
|
|
55
|
+
var lastCall = (0, _find.default)(_context = lastCallsRef.current).call(_context, function (entry) {
|
|
56
|
+
return itemEquality.call(array, item, entry[0]);
|
|
57
|
+
});
|
|
58
|
+
var result = lastCall ? lastCall[1] : mapper.call(array, item, -1, array);
|
|
59
|
+
thisCalls.push([item, result]);
|
|
60
|
+
return result;
|
|
61
|
+
}));
|
|
62
|
+
}, [itemEqualityRef, lastCallsRef, mapperRef, thisCallsRef]);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=useMemoMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMemoMap.js","names":["useMemoMap","mapper","itemEquality","itemEqualityRef","useValueRef","lastCallsRef","useRef","mapperRef","thisCalls","usePrevious","current","useEffect","thisCallsRef","useCallback","array","item","thisCall","entry","call","lastCall","result","push"],"sources":["../../src/useMemoMap.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport usePrevious from './private/usePrevious';\nimport useValueRef from './private/useValueRef';\n\ntype UseMemoMapOptions<T> = {\n itemEquality?: (this: readonly T[], x: T, y: T) => boolean;\n};\n\n/**\n * Creates a memoized mapping function.\n *\n * Unlike `React.useMemo`, the mapping function can be called multiple times in a single render loop.\n * All calls to the mapping function will be memoized.\n *\n * The memoized arguments and return values will survive next render.\n *\n * When the mapping function change, all memoized values will be invalidated.\n */\nexport default function useMemoMap<T = unknown, R = unknown>(\n mapper: (this: readonly T[], item: T, index: -1, array: readonly T[]) => R,\n { itemEquality = Object.is }: UseMemoMapOptions<T> = {}\n): (array: readonly T[]) => readonly R[] {\n const itemEqualityRef = useValueRef(itemEquality);\n const lastCallsRef = useRef<readonly [T, R][]>([]);\n const mapperRef = useValueRef(mapper);\n const thisCalls: [T, R][] = [];\n\n if (usePrevious(mapper) !== mapper) {\n lastCallsRef.current = [];\n }\n\n useEffect(() => {\n lastCallsRef.current = Object.freeze(thisCalls);\n });\n\n const thisCallsRef = useValueRef(thisCalls);\n\n return useCallback<(array: readonly T[]) => readonly R[]>(\n (array: readonly T[]) => {\n const { current: itemEquality } = itemEqualityRef;\n const { current: mapper } = mapperRef;\n const { current: thisCalls } = thisCallsRef;\n\n return <readonly R[]>Object.freeze(\n array.map<R>(item => {\n const thisCall = thisCalls.find(entry => itemEquality.call(array, item, entry[0]));\n\n // If this call has memoized in the current render loop, use the memoized return value.\n if (thisCall) {\n return thisCall[1];\n }\n\n const lastCall = lastCallsRef.current.find(entry => itemEquality.call(array, item, entry[0]));\n const result = lastCall ? lastCall[1] : mapper.call(array, item, -1, array);\n\n thisCalls.push([item, result]);\n\n return result;\n })\n );\n },\n [itemEqualityRef, lastCallsRef, mapperRef, thisCallsRef]\n );\n}\n"],"mappings":";;;;;;;;;;;;AAAA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,UAAU,CAChCC,MAA0E,EAEnC;EAAA,+EADc,CAAC,CAAC;IAAA,yBAArDC,YAAY;IAAZA,YAAY;EAEd,IAAMC,eAAe,GAAG,IAAAC,oBAAW,EAACF,YAAY,CAAC;EACjD,IAAMG,YAAY,GAAG,IAAAC,aAAM,EAAoB,EAAE,CAAC;EAClD,IAAMC,SAAS,GAAG,IAAAH,oBAAW,EAACH,MAAM,CAAC;EACrC,IAAMO,SAAmB,GAAG,EAAE;EAE9B,IAAI,IAAAC,oBAAW,EAACR,MAAM,CAAC,KAAKA,MAAM,EAAE;IAClCI,YAAY,CAACK,OAAO,GAAG,EAAE;EAC3B;EAEA,IAAAC,gBAAS,EAAC,YAAM;IACdN,YAAY,CAACK,OAAO,GAAG,qBAAcF,SAAS,CAAC;EACjD,CAAC,CAAC;EAEF,IAAMI,YAAY,GAAG,IAAAR,oBAAW,EAACI,SAAS,CAAC;EAE3C,OAAO,IAAAK,kBAAW,EAChB,UAACC,KAAmB,EAAK;IACvB,IAAiBZ,YAAY,GAAKC,eAAe,CAAzCO,OAAO;IACf,IAAiBT,MAAM,GAAKM,SAAS,CAA7BG,OAAO;IACf,IAAiBF,SAAS,GAAKI,YAAY,CAAnCF,OAAO;IAEf,OAAqB,qBACnB,kBAAAI,KAAK,OAALA,KAAK,EAAQ,UAAAC,IAAI,EAAI;MAAA;MACnB,IAAMC,QAAQ,GAAG,mBAAAR,SAAS,OAATA,SAAS,EAAM,UAAAS,KAAK;QAAA,OAAIf,YAAY,CAACgB,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAEE,KAAK,CAAC,CAAC,CAAC,CAAC;MAAA,EAAC;;MAElF;MACA,IAAID,QAAQ,EAAE;QACZ,OAAOA,QAAQ,CAAC,CAAC,CAAC;MACpB;MAEA,IAAMG,QAAQ,GAAG,8BAAAd,YAAY,CAACK,OAAO,iBAAM,UAAAO,KAAK;QAAA,OAAIf,YAAY,CAACgB,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAEE,KAAK,CAAC,CAAC,CAAC,CAAC;MAAA,EAAC;MAC7F,IAAMG,MAAM,GAAGD,QAAQ,GAAGA,QAAQ,CAAC,CAAC,CAAC,GAAGlB,MAAM,CAACiB,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAE,CAAC,CAAC,EAAED,KAAK,CAAC;MAE3EN,SAAS,CAACa,IAAI,CAAC,CAACN,IAAI,EAAEK,MAAM,CAAC,CAAC;MAE9B,OAAOA,MAAM;IACf,CAAC,CAAC,CACH;EACH,CAAC,EACD,CAACjB,eAAe,EAAEE,YAAY,EAAEE,SAAS,EAAEK,YAAY,CAAC,CACzD;AACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["useMemoMap"],"sources":["../../src/index.ts"],"sourcesContent":["import useMemoMap from './useMemoMap';\n\nexport { useMemoMap };\n"],"mappings":"AAAA,OAAOA,UAAU,MAAM,cAAc;AAErC,SAASA,UAAU"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrevious.js","names":["useEffect","useRef","usePrevious","value","ref","current"],"sources":["../../../src/private/usePrevious.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport default function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T>();\n\n useEffect(() => {\n ref.current = value;\n });\n\n return ref.current;\n}\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAEzC,eAAe,SAASC,WAAW,CAAIC,KAAQ,EAAiB;EAC9D,IAAMC,GAAG,GAAGH,MAAM,EAAK;EAEvBD,SAAS,CAAC,YAAM;IACdI,GAAG,CAACC,OAAO,GAAGF,KAAK;EACrB,CAAC,CAAC;EAEF,OAAOC,GAAG,CAACC,OAAO;AACpB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import _Object$create from "@babel/runtime-corejs3/core-js-stable/object/create";
|
|
2
|
+
import { useMemo, useRef } from 'react';
|
|
3
|
+
export default function useValueRef(value) {
|
|
4
|
+
var ref = useRef();
|
|
5
|
+
var readOnlyRef = useMemo(function () {
|
|
6
|
+
return _Object$create({}, {
|
|
7
|
+
current: {
|
|
8
|
+
get: function get() {
|
|
9
|
+
return ref.current;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}, [ref]);
|
|
14
|
+
ref.current = value;
|
|
15
|
+
return readOnlyRef;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=useValueRef.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useValueRef.js","names":["useMemo","useRef","useValueRef","value","ref","readOnlyRef","current","get"],"sources":["../../../src/private/useValueRef.ts"],"sourcesContent":["import { useMemo, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nexport default function useValueRef<T>(value: T): RefObject<T> & { current: T } {\n const ref = useRef<T>();\n const readOnlyRef = useMemo(() => Object.create({}, { current: { get: () => ref.current } }), [ref]);\n\n ref.current = value;\n\n return readOnlyRef;\n}\n"],"mappings":";AAAA,SAASA,OAAO,EAAEC,MAAM,QAAQ,OAAO;AAGvC,eAAe,SAASC,WAAW,CAAIC,KAAQ,EAAiC;EAC9E,IAAMC,GAAG,GAAGH,MAAM,EAAK;EACvB,IAAMI,WAAW,GAAGL,OAAO,CAAC;IAAA,OAAM,eAAc,CAAC,CAAC,EAAE;MAAEM,OAAO,EAAE;QAAEC,GAAG,EAAE;UAAA,OAAMH,GAAG,CAACE,OAAO;QAAA;MAAC;IAAE,CAAC,CAAC;EAAA,GAAE,CAACF,GAAG,CAAC,CAAC;EAEpGA,GAAG,CAACE,OAAO,GAAGH,KAAK;EAEnB,OAAOE,WAAW;AACpB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import _Object$is from "@babel/runtime-corejs3/core-js-stable/object/is";
|
|
2
|
+
import _Object$freeze from "@babel/runtime-corejs3/core-js-stable/object/freeze";
|
|
3
|
+
import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map";
|
|
4
|
+
import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find";
|
|
5
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
6
|
+
import usePrevious from './private/usePrevious';
|
|
7
|
+
import useValueRef from './private/useValueRef';
|
|
8
|
+
/**
|
|
9
|
+
* Creates a memoized mapping function.
|
|
10
|
+
*
|
|
11
|
+
* Unlike `React.useMemo`, the mapping function can be called multiple times in a single render loop.
|
|
12
|
+
* All calls to the mapping function will be memoized.
|
|
13
|
+
*
|
|
14
|
+
* The memoized arguments and return values will survive next render.
|
|
15
|
+
*
|
|
16
|
+
* When the mapping function change, all memoized values will be invalidated.
|
|
17
|
+
*/
|
|
18
|
+
export default function useMemoMap(mapper) {
|
|
19
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
20
|
+
_ref$itemEquality = _ref.itemEquality,
|
|
21
|
+
itemEquality = _ref$itemEquality === void 0 ? _Object$is : _ref$itemEquality;
|
|
22
|
+
var itemEqualityRef = useValueRef(itemEquality);
|
|
23
|
+
var lastCallsRef = useRef([]);
|
|
24
|
+
var mapperRef = useValueRef(mapper);
|
|
25
|
+
var thisCalls = [];
|
|
26
|
+
if (usePrevious(mapper) !== mapper) {
|
|
27
|
+
lastCallsRef.current = [];
|
|
28
|
+
}
|
|
29
|
+
useEffect(function () {
|
|
30
|
+
lastCallsRef.current = _Object$freeze(thisCalls);
|
|
31
|
+
});
|
|
32
|
+
var thisCallsRef = useValueRef(thisCalls);
|
|
33
|
+
return useCallback(function (array) {
|
|
34
|
+
var itemEquality = itemEqualityRef.current;
|
|
35
|
+
var mapper = mapperRef.current;
|
|
36
|
+
var thisCalls = thisCallsRef.current;
|
|
37
|
+
return _Object$freeze(_mapInstanceProperty(array).call(array, function (item) {
|
|
38
|
+
var _context;
|
|
39
|
+
var thisCall = _findInstanceProperty(thisCalls).call(thisCalls, function (entry) {
|
|
40
|
+
return itemEquality.call(array, item, entry[0]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// If this call has memoized in the current render loop, use the memoized return value.
|
|
44
|
+
if (thisCall) {
|
|
45
|
+
return thisCall[1];
|
|
46
|
+
}
|
|
47
|
+
var lastCall = _findInstanceProperty(_context = lastCallsRef.current).call(_context, function (entry) {
|
|
48
|
+
return itemEquality.call(array, item, entry[0]);
|
|
49
|
+
});
|
|
50
|
+
var result = lastCall ? lastCall[1] : mapper.call(array, item, -1, array);
|
|
51
|
+
thisCalls.push([item, result]);
|
|
52
|
+
return result;
|
|
53
|
+
}));
|
|
54
|
+
}, [itemEqualityRef, lastCallsRef, mapperRef, thisCallsRef]);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=useMemoMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMemoMap.js","names":["useCallback","useEffect","useRef","usePrevious","useValueRef","useMemoMap","mapper","itemEquality","itemEqualityRef","lastCallsRef","mapperRef","thisCalls","current","thisCallsRef","array","item","thisCall","entry","call","lastCall","result","push"],"sources":["../../src/useMemoMap.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport usePrevious from './private/usePrevious';\nimport useValueRef from './private/useValueRef';\n\ntype UseMemoMapOptions<T> = {\n itemEquality?: (this: readonly T[], x: T, y: T) => boolean;\n};\n\n/**\n * Creates a memoized mapping function.\n *\n * Unlike `React.useMemo`, the mapping function can be called multiple times in a single render loop.\n * All calls to the mapping function will be memoized.\n *\n * The memoized arguments and return values will survive next render.\n *\n * When the mapping function change, all memoized values will be invalidated.\n */\nexport default function useMemoMap<T = unknown, R = unknown>(\n mapper: (this: readonly T[], item: T, index: -1, array: readonly T[]) => R,\n { itemEquality = Object.is }: UseMemoMapOptions<T> = {}\n): (array: readonly T[]) => readonly R[] {\n const itemEqualityRef = useValueRef(itemEquality);\n const lastCallsRef = useRef<readonly [T, R][]>([]);\n const mapperRef = useValueRef(mapper);\n const thisCalls: [T, R][] = [];\n\n if (usePrevious(mapper) !== mapper) {\n lastCallsRef.current = [];\n }\n\n useEffect(() => {\n lastCallsRef.current = Object.freeze(thisCalls);\n });\n\n const thisCallsRef = useValueRef(thisCalls);\n\n return useCallback<(array: readonly T[]) => readonly R[]>(\n (array: readonly T[]) => {\n const { current: itemEquality } = itemEqualityRef;\n const { current: mapper } = mapperRef;\n const { current: thisCalls } = thisCallsRef;\n\n return <readonly R[]>Object.freeze(\n array.map<R>(item => {\n const thisCall = thisCalls.find(entry => itemEquality.call(array, item, entry[0]));\n\n // If this call has memoized in the current render loop, use the memoized return value.\n if (thisCall) {\n return thisCall[1];\n }\n\n const lastCall = lastCallsRef.current.find(entry => itemEquality.call(array, item, entry[0]));\n const result = lastCall ? lastCall[1] : mapper.call(array, item, -1, array);\n\n thisCalls.push([item, result]);\n\n return result;\n })\n );\n },\n [itemEqualityRef, lastCallsRef, mapperRef, thisCallsRef]\n );\n}\n"],"mappings":";;;;AAAA,SAASA,WAAW,EAAEC,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAEtD,OAAOC,WAAW,MAAM,uBAAuB;AAC/C,OAAOC,WAAW,MAAM,uBAAuB;AAM/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe,SAASC,UAAU,CAChCC,MAA0E,EAEnC;EAAA,+EADc,CAAC,CAAC;IAAA,yBAArDC,YAAY;IAAZA,YAAY;EAEd,IAAMC,eAAe,GAAGJ,WAAW,CAACG,YAAY,CAAC;EACjD,IAAME,YAAY,GAAGP,MAAM,CAAoB,EAAE,CAAC;EAClD,IAAMQ,SAAS,GAAGN,WAAW,CAACE,MAAM,CAAC;EACrC,IAAMK,SAAmB,GAAG,EAAE;EAE9B,IAAIR,WAAW,CAACG,MAAM,CAAC,KAAKA,MAAM,EAAE;IAClCG,YAAY,CAACG,OAAO,GAAG,EAAE;EAC3B;EAEAX,SAAS,CAAC,YAAM;IACdQ,YAAY,CAACG,OAAO,GAAG,eAAcD,SAAS,CAAC;EACjD,CAAC,CAAC;EAEF,IAAME,YAAY,GAAGT,WAAW,CAACO,SAAS,CAAC;EAE3C,OAAOX,WAAW,CAChB,UAACc,KAAmB,EAAK;IACvB,IAAiBP,YAAY,GAAKC,eAAe,CAAzCI,OAAO;IACf,IAAiBN,MAAM,GAAKI,SAAS,CAA7BE,OAAO;IACf,IAAiBD,SAAS,GAAKE,YAAY,CAAnCD,OAAO;IAEf,OAAqB,eACnB,qBAAAE,KAAK,OAALA,KAAK,EAAQ,UAAAC,IAAI,EAAI;MAAA;MACnB,IAAMC,QAAQ,GAAG,sBAAAL,SAAS,OAATA,SAAS,EAAM,UAAAM,KAAK;QAAA,OAAIV,YAAY,CAACW,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAEE,KAAK,CAAC,CAAC,CAAC,CAAC;MAAA,EAAC;;MAElF;MACA,IAAID,QAAQ,EAAE;QACZ,OAAOA,QAAQ,CAAC,CAAC,CAAC;MACpB;MAEA,IAAMG,QAAQ,GAAG,iCAAAV,YAAY,CAACG,OAAO,iBAAM,UAAAK,KAAK;QAAA,OAAIV,YAAY,CAACW,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAEE,KAAK,CAAC,CAAC,CAAC,CAAC;MAAA,EAAC;MAC7F,IAAMG,MAAM,GAAGD,QAAQ,GAAGA,QAAQ,CAAC,CAAC,CAAC,GAAGb,MAAM,CAACY,IAAI,CAACJ,KAAK,EAAEC,IAAI,EAAE,CAAC,CAAC,EAAED,KAAK,CAAC;MAE3EH,SAAS,CAACU,IAAI,CAAC,CAACN,IAAI,EAAEK,MAAM,CAAC,CAAC;MAE9B,OAAOA,MAAM;IACf,CAAC,CAAC,CACH;EACH,CAAC,EACD,CAACZ,eAAe,EAAEC,YAAY,EAAEC,SAAS,EAAEG,YAAY,CAAC,CACzD;AACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function usePrevious<T>(value: T): T | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type UseMemoMapOptions<T> = {
|
|
2
|
+
itemEquality?: (this: readonly T[], x: T, y: T) => boolean;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Creates a memoized mapping function.
|
|
6
|
+
*
|
|
7
|
+
* Unlike `React.useMemo`, the mapping function can be called multiple times in a single render loop.
|
|
8
|
+
* All calls to the mapping function will be memoized.
|
|
9
|
+
*
|
|
10
|
+
* The memoized arguments and return values will survive next render.
|
|
11
|
+
*
|
|
12
|
+
* When the mapping function change, all memoized values will be invalidated.
|
|
13
|
+
*/
|
|
14
|
+
export default function useMemoMap<T = unknown, R = unknown>(mapper: (this: readonly T[], item: T, index: -1, array: readonly T[]) => R, { itemEquality }?: UseMemoMapOptions<T>): (array: readonly T[]) => readonly R[];
|
|
15
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "use-memo-map",
|
|
3
|
+
"version": "0.0.0-main.18b69b6",
|
|
4
|
+
"description": "",
|
|
5
|
+
"files": [
|
|
6
|
+
"./lib/*"
|
|
7
|
+
],
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./lib/esmodules/index.js",
|
|
11
|
+
"require": "./lib/commonjs/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./useMemoMap": {
|
|
14
|
+
"import": "./lib/esmodules/useMemoMap.js",
|
|
15
|
+
"require": "./lib/commonjs/useMemoMap.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"main": "lib/index.js",
|
|
19
|
+
"typings": "lib/types/index.d.ts",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "npm run build:babel && npm run build:typescript",
|
|
22
|
+
"build:babel": "npm run build:babel:commonjs && npm run build:babel:esmodules",
|
|
23
|
+
"build:babel:commonjs": "babel src --config-file ./babel.commonjs.config.json --extensions .ts,.tsx --out-dir ./lib/commonjs/",
|
|
24
|
+
"build:babel:esmodules": "babel src --config-file ./babel.esmodules.config.json --extensions .ts,.tsx --out-dir ./lib/esmodules/",
|
|
25
|
+
"build:typescript": "tsc --project ./src/tsconfig.declaration.json",
|
|
26
|
+
"precommit": "eslint ./src/",
|
|
27
|
+
"test": "jest"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/compulim/use-memo-map.git"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"react",
|
|
35
|
+
"react-hooks"
|
|
36
|
+
],
|
|
37
|
+
"author": "William Wong (https://github.com/compulim)",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/compulim/use-memo-map/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/compulim/use-memo-map#readme",
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/cli": "^7.20.7",
|
|
45
|
+
"@babel/core": "^7.20.7",
|
|
46
|
+
"@babel/plugin-transform-runtime": "^7.19.6",
|
|
47
|
+
"@babel/preset-env": "^7.20.2",
|
|
48
|
+
"@babel/preset-typescript": "^7.18.6",
|
|
49
|
+
"@testing-library/react-hooks": "^8.0.1",
|
|
50
|
+
"@types/react": "^17.0.52",
|
|
51
|
+
"jest": "^29.3.1",
|
|
52
|
+
"jest-environment-jsdom": "^29.3.1",
|
|
53
|
+
"react": "^17.0.2",
|
|
54
|
+
"react-test-renderer": "^17.0.2",
|
|
55
|
+
"typescript": "^4.9.4"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": ">=16.9.0"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@babel/runtime-corejs3": "^7.20.7"
|
|
62
|
+
}
|
|
63
|
+
}
|