use-memo-map v0.1.0
use-memo-map
Memoizes calls to array map function similar to React.useMemo
. Memoized results will survive next render.
Background
tl;dr, React.useMemo()
cache a single call. useMemoAll()
cache multiple calls.
If you have a variable-length array and would like to cache Array.map
like useMemo
, you can use useMemoAll
to cache all calls.
API
type UseMemoMapOptions<T> = {
itemEquality?: (this: readonly T[], x: T, y: T) => boolean;
};
function useMemoMap<T = unknown, R = unknown>(
mapper: (this: readonly T[], item: T, index: -1, array: readonly T[]) => R,
{ itemEquality = Object.is }: UseMemoMapOptions<T> = {}
): (array: readonly T[]) => readonly R[] {}
For example, converting a number
array into a string
array.
function useMemoMap(
mapper: (item: number, index: -1, array: readonly number[]) => string
): (array: readonly number[]) => readonly string[];
How to use
import { useMemoMap } from 'use-memo-map';
const MyComponent = () => {
const multiplyBy10 = useCallback(value => {
// Calls to this function will be memoized based on its first argument.
// You can do expensive calls here.
return value * 10;
}, []);
// useMemoMap() will return a function similar to Array.map().
const map = useMemoMap(multiplyBy10);
const output = map([1, 2, 3]); // Returns [10, 20, 30].
return ...;
};
export default MyComponent;
Behaviors
Invalidate all results when mapper function change
If the first argument (a.k.a. the mapper function) changed, it will invalidate all results. This is by design.
You should always use useCallback
or useMemo
to cache the mapper function.
const MyComponent = () => {
// ❌ In the next line, multiplyBy10() is a new instance on every render of <MyComponent>.
const multiplyBy10 = value => value * 10;
// ✔️ You should cache multiplyBy10() by React.useCallback().
const multiplyBy10 = useCallback(value => value * 10, []);
const map = useMemoMap(multiplyBy10);
// Calls to map() will not survive across render calls if multiplyBy10() changed.
const output = map([1, 2, 3]);
// ...
};
Single cache pool for multiple calls
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".
const MyComponent = () => {
// ...
const output1 = map([1, 2, 3]); // Return [10, 20, 30].
const output2 = map([1, 2, 3]); // Return [10, 20, 30] without calling `multiplyBy10`.
// ...
};
Note: the "cache pool" will be saved during useEffect()
callback. This is similar to usePrevious()
hook.
Index is always -1
Unlike Array.map()
which pass the index
value as second argument, useMemoAll()
will always pass -1
. This is by design.
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.
If you understand this risk, call this.indexOf(item)
to get the index
value.
Returns read-only result
React loves immutability. By default, the array we returned is frozen (a.k.a. read-only).
If you prefer mutable array, call [...result]
to clone the array.
Custom equality function
We use Object.is
for equality check. You can provide a different equality check via options.itemEquality
.
Contributions
Like us? Star us.
Want to make it better? File us an issue.
Don't like something you see? Submit a pull request.
1 month ago
1 month ago
1 month ago
1 month ago
5 months ago
5 months ago
10 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
10 months ago
7 months ago
7 months ago
7 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago