preface

Because React’s functional components are easy to use (as opposed to the Class component), I’ll focus on using functional components to run development. In this blog series, I’m going to share what I’ve learned about the Hook apis. The Hooks series includes the following:

  • useState
  • useReducer
  • useContext
  • useEffect
  • useMemo
  • useRef
  • Customize the Hook

Memo

In the class era, we used to do a shallow comparison of data using pureComponent. With the Hook feature introduced, we could use Memo to improve performance.

But before we do that, let’s do an experiment

import React, { useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [n, setN] = useState(0);
  const [m, setM] = useState(10);
  console.log("Execute outermost box.");
  return (
    <>
      <div>Outermost box<Child1 value={n} />
        <Child2 value={m} />
        <button
          onClick={()= > {
            setN(n + 1);
          }}
        >
          n+1
        </button>
        <button
          onClick={()= > {
            setM(m + 1);
          }}
        >
          m+1
        </button>
      </div>
    </>
  );
}
function Child1(props) {
  console.log("Executing child component 1");
  return <div>N: {props. Value} on child component 1</div>;
}
function Child2(props) {
  console.log("Executing child component 2");
  return <div>M: {props. Value} on child component 2</div>;
}

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

In the code above, I set up two child components, respectively read n and M on the parent component, and then set two click buttons on the parent component, when clicked, set n and M plus 1 respectively. Here is the result of the log console when first rendered

Execute the outermost box execute the child component 1 execute the child component 2Copy the code

As imagined, when render, first enter App function, execute, find two child functions inside, execute, create virtual DOM, create entity DOM, and finally render the picture to the page.

Using Memo Optimization

When I click the n+1 button, the n in state must be +1, which will also retrigger the render and update the new n to the view.Let’s go back to the console

Execute outermost box execute subcomponent 1 execute subcomponent 2 + execute outermost box execute subcomponent 1 execute subcomponent 2 + execute subcomponent 1 execute subcomponent 2Copy the code

You’ll notice that subcomponent 2 is also rendered. Obviously React reexecutes all functions, including subcomponent 2 that didn’t have n data.

How to optimize? We can change the subcomponent to the following code using memo

const Child1 = React.memo((props) = > {
  console.log("Executing child component 1");
  return <div>N: {props. Value} on child component 1</div>;
});

const Child2 = React.memo((props) = > {
  console.log("Executing child component 2");
  return <div>M: {props. Value} on child component 2</div>;
});
Copy the code

Try clicking again?

I'm going to execute the outermost box I'm going to execute the child component 1 I'm going to execute the child component 2 + I'm going to execute the outermost box + I'm going to execute the child component 1Copy the code

You will notice that no child component 2 is executed

React will only execute the components with the state change, and then use the last memo function if the components have not changed.

bugs

The above code is optimized for performance, but there is a bug

The code above is controlled by the parent component

 <Child2 value={m} onClick={addM} />//addM is a function to modify MCopy the code

Click the button to make n+1

I've executed the outermost box I've executed the child component 1 I've executed the child component 2 + I've executed the outermost box + I've executed the child component 1 + I've executed the child component 2Copy the code

Execute child component 2 again.

Why is that? Because App re-executes, it changes the address of the addM function (functions are complex data types), and addM is passed as props to subcomponent 2, which causes the subcomponent 2 function to re-execute.

useMemo

This is where useMemo comes in.

useMemo(()=>{},[])

UseMemo takes two arguments, a function and an array (which is actually a dependency) that contains a return function and an array containing the dependency.

const addM = useMemo(() = > {
    return () = > {
      setM({ m: m.m + 1 });
    };
  }, [m]); // Monitors m changes
Copy the code

It is used in the same way as useEffect.

useCallback

The code up there is weird

useMemo(() = > {
    return () = > {
      setM({ m: m.m + 1 });
    };
  }, [m])
Copy the code

React gives us syntax candy, useCallback. It reads like this

  const addM = useCallback(() = > {
    setM({ m: m.m + 1 });
  }, [m]);
Copy the code

Doesn’t it look more normal?

The final code

import React, { useCallback, useMemo, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [n, setN] = useState(0);
  const [m, setM] = useState({ m: 1 });
  console.log("Execute outermost box.");
  const addN = useMemo(() = > {
    return () = > {
      setN(n + 1);
    };
  }, [n]);
  const addM = useCallback(() = > {
    setM({ m: m.m + 1 });
  }, [m]);
  return (
    <>
      <div>Outermost box<Child1 value={n} click={addN} />
        <Child2 value={m} click={addM} />
        <button onClick={addN}>n+1</button>
        <button onClick={addM}>m+1</button>
      </div>
    </>
  );
}
const Child1 = React.memo((props) = > {
  console.log("Executing child component 1");
  return <div>N: {props. Value} on child component 1</div>;
});

const Child2 = React.memo((props) = > {
  console.log("Executing child component 2");
  return <div>M: {props.value.m} on child component 2</div>;
});

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

It is recommended to copy directly to Codesanbox for viewing

conclusion

  • usememoCan help us optimize the performance of thereactThere is no need to perform unnecessary functions
  • Passed to a child component because the address of a complex data type may changepropsIt’s going to change, and it’s still going to execute unnecessary functions, so it’s going to be useduseMemoThis API
  • useCallbackisuseMemoThe syntactic sugar