unpredictable
unpredictable

Reputation: 360

How to optimize my code with useMemo in React?

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

Answers (2)

Prateek Thapa
Prateek Thapa

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

Ori Drori
Ori Drori

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

Related Questions