The original address: www.linxiangjun.com/memoization…

Recently, while studying React Hook optimization, it was found that Memoization techniques are widely used officially, such as useCallback and useMemo apis, which are used to return memoized versions and memoized values of functions, respectively. Memoization is not a new technology, but an optimization technique. Wikipedia defines Memoization as:

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

To put it simply, Memoization is to store the previous value, then compare the newly passed value with the stored value on each update, and then return the stored value or the new value based on whether the comparison results are the same. React uses this technique primarily to avoid unnecessary repetitive rendering.

Application in Redux

The resELECT we are using to optimize Redux data is Memoization technology. This article analyzes the resELECT source code and can learn how resELECT is using this technology to optimize Redux data.

React Hook

Now that we’re familiar with Memoization’s definition, let’s take a look at how we can use this technology in real life. In React Hook, the useEffect method can use this technique to optimize component rendering. This example can be viewed in the CodeSandbox :smile:.

We know that useEffect’s second array argument exists as an effect dependency, and if the dependency array changes, useEffect is reexecuted. UseEffect uses === to compare whether the array parameters are equal. Using objects. Assign or splice will return a new heap to store the reference value, causing the useEffect method to be repeated without changing the value itself. To avoid this, let’s look at what to do.

First, let’s take a look at the useRef method, which is very similar to createRef in that it creates a mutable ref object. In function components, because the REF object remains constant throughout the life of the component, we can use it to store values that are not affected by component re-render. So, in the following function, we use the ref object to store the second array parameter passed in useEffect.

import React, { useRef } from "react";
import { isEqual } from "lodash";

function useDeepCompareMemoize(value) {
  const ref = useRef();

  if(! isEqual(value, ref.current)) { ref.current = value; }return ref.current;
}
Copy the code

As you can see, I’m using the deep comparison method in lodash, isEqual, to compare two values, which can be customized or customized as required.

Next write a custom Hook to use useEffect:

function useDeepCompareEffect(callback, dependencies) {
  useEffect(callback, useDeepCompareMemoize(dependencies));
}

export default useDeepCompareEffect;
Copy the code

Then use useDeepCompareEffect instead of useEffect.

The complete code used is as follows:

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import useDeepCompareEffect from "./useDeepCompareEffect.js";

import "./styles.css";

const tomCat = {
  name: "Tom".race: "cat"
};
const jerryMouse = {
  name: "Jerry".race: "mouse"
};

function App() {
  const [character, updateCharacter] = useState(tomCat);
  const effectCount = useRef(0);
  const deepCompareEffectCount = useRef(0);

  useEffect((a)= > {
    effectCount.current++;
  }, [character]);

  useDeepCompareEffect((a)= > {
    deepCompareEffectCount.current++;
  }, [character]);

  const changeStar = (a)= > {
    const star = character.name === "Tom" ? jerryMouse : tomCat;
    updateCharacter(star);
  };

  const assignObj = (a)= > {
    updateCharacter(Object.assign({}, character));
  };

  return (
    <div className="App">
      <p>Hello, useEffect</p>
      <p className="star">
        {character.name} {character.race} {character.friend}
      </p>
      <p>useEffect count = {effectCount.current}</p>
      <p>deepCompareEffectCount count = {deepCompareEffectCount.current}</p>
      <p>
        <button onClick={changeStar}>Change star</button>
      </p>
      <p>
        <button onClick={assignObj}>Click</button>
      </p>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Copy the code

Similarly, if you Click on the Click button, the useEffect method is executed every time, whereas the useDeepCompareEffect method is executed only once. However, it is important to note that this optimization is not a panacea, but should be used on a case-by-case basis. Since deep comparisons are inherently costly to performance, use them on demand. UseCallback and useMemo, for example, are best used when double-counting is required. Improper use of useCallback and useMemo may have adverse effects in other scenarios, and the references at the end of this article are recommended.

reference

  1. use-deep-compare-effect
  2. memoize-one
  3. You’re overusing useMemo: Rethinking Hooks memoization