Reputation: 360
I have a situation in React with the useMemo()
hook.
I have an array object called a
. This object contains multiple objects. This array is large. When it contains b
and c
objects, a
looks like a = [b,c]
. I am doing heavy computation on array a
. Here is my code snippet:
const computedValue = useMemo(() => {
return a.map(key,() => {
anotherHighComputationFunc(key);
});
},[a])
const anotherHighComputationFunc = useMemo((key) => {
// ..... do some expensive computation
return someValue;
},[key]);
If a reference of a
does not change, I do not compute the value again, I just return the precomputed value. If a reference of a
changes then I will look into the array a
to check if some key has changed its reference inside the array. If for some key reference has not changed then return the precomputed value for that key, otherwise do computation for that key.
The issue is that the rule of hook fails as I am using a hook inside another hook. So it throws me an error. Is there any other way round to do memoization at 2 levels?
Upvotes: 1
Views: 1542
Reputation: 4938
The rules of Hooks clearly state: Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function.
Rather than using a hook for your anotherHighComputationFunc
, create a cache
object which stores the values of the function and return it if it is inside the cache object.
const cache = {};
function anotherHighComputationFunc(key) {
if (cache[key]) return cache[key];
/** anotherHighComputation calculation here */
cache[key] = anotherHighComputationValue;
return anotherHighComputationValue
}
const computedValue = useMemo(() => {
return a.map(key,() => {
anotherHighComputationFunc(key);
});
},[a])
Upvotes: 0
Reputation: 191986
The useMemo()
hook won't help you here, because it has a cache size of 1, and you can't call hooks inside other hooks. In addition, you don't need to memoize the function, but the results of the call.
You can create a simple memoization wrapper using WeakMap
that will allow the garbage collector to clean the values when not needed.
Note: WeakMap keys must be objects.
const weakMemo = fn => {
const map = new WeakMap();
return obj => {
if (map.has(obj)) return map.get(obj);
const res = fn(obj);
map.set(obj, res);
return rs;
}
};
// the useMemo is needed here to preserve the instance of the memoized function with the cached values
const computeKey = useMemo(() => weakMemo((key) => {
// ..... do some expensive computation
return someValue;
}), [anotherHighComputationFunc]);
const computedValue = useMemo(() => a.map(computeKey), [a, computeKey]);
Upvotes: 1