The preface

In this section, we will focus on two hooks: useCallback and useMemo, and understand custom hooks.

UseCallback, repeatedly render the solution

The “weirdness” of infinite requests

Before we introduce the react hook, let’s look at some code that beginners can easily write:

const [detail, setDetail] = useState();

// Request data through the background interface and set to state
const fetchDetail = async() = > {try {
        const res = awaitxxxx; setDetail(res? .data || {}) }catch(err) {
        // xxx}};// Expect something similar to calling the interface once in the componentDidMount lifecycle to get details
useEffect(() = > {
    fetchDetail()
}, [fetchDetail])
Copy the code

This code actually causes serious bugs, and when we open the Network panel, we see the browser frantically making requests to the background.

This is because when rerendering, the fetchDetail function is recreated and placed in the DEPS dependency array. As we mentioned above, react versus DEps is essentially done with object. is. Two renderings create two functions, and comparing references to the two functions naturally results in unequal results.

usage

const memoFn = useCallback(fn, deps);
Copy the code

Such as:

const showUA = useCallback(function () {
  console.log(navigator.userAgent); } []);Copy the code

The principle of

The essence of this is to save a function outside the component and return a pointer to the function. Here we can roughly assume that useCallback prevents functions from being created repeatedly on each render.

Since useCallback returns a reference to a function, that reference can be safely placed in the DEPS array. That is, every time you run the useCallback hook, you get the same function reference, pointing to the same address in the heap. If the two references compare equally, the effectFn will not be run again.

We will modify the beginning of the strange code as follows:

const [detail, setDetail] = useState();

const fetchDetail = useCallback(async() = > {try {
        const res = awaitxxxx; setDetail(res? .data || {}) }catch(err) {
        // xxx}}); useEffect(() = > {
    fetchDetail()
}, [fetchDetail])
Copy the code

UseMemo, Computed in Raect

UseMemo is a superset of useCallback.

UseMemo is used to cache the execution results of a function. This is very similar to computed properties in VUE. If the result of a function’s execution returns a function, it is equivalent to caching a function. This is why useMemo is a superset of useCallback, as follows:

const memoFn = useCallback(() = > console.log('111'), [])

const memoFn2 = useMemo(() = > () = > console.log('111'), [])
Copy the code

Of course, we don’t normally do this. Let the useCalback cache function reference the useMemo cache function execute the result. They are easy to understand.

UseMemo is commonly used as follows:

const memoValue = useMemo(() = > a * b, [a, b]);
Copy the code

React.memo + useCallback to avoid unnecessary rendering by subcomponents

This is a common performance tuning tool.

The react. memo can be useMemo instead of useMemo. Personally, I prefer to use the React.memo for component caching.

Let’s do a little experiment:

// App.tsx
import React, { useState } from "react";
import Sub from "./Sub";

function App() {
  const [num, increaseNum] = useState(0);

  const logFn = () = > console.log("logging...");

  return (
    <>
      <Sub logFn={logFn} />
      <h1>{num}</h1>
      <button onClick={()= >Increasement ((prev) => prev + 1)}> The number increases</button>
    </>
  );
}

export default App;


// Sub.tsx
import React, { useEffect } from "react";

export interface SubProps {
  logFn: () = > void;
}

const Sub: React.FC<SubProps> = ({ logFn }) = > {
  useEffect(() = > {
    console.log("sub render");
  });

  return <button onClick={logFn}>print</button>;
};

export default Sub;
Copy the code

A very simple example, when running the code above, you can find: the parent component clicks the number +1, the child component inexplicately gets rerendered and prints sub Render.

So here we need two steps:

  1. Caching child components to avoid unconditional rerendering of child components is a must;
  2. If the child component uses a function declared in the parent component, the function is cached to avoid “parent re-render -> recreate function -> affected child re-render” problems.

Key parts of the above code are modified as follows (other parts remain unchanged) :

// App.tsx
const logFn = useCallback(() = > console.log("logging..."), []);

// Sub.tsx
export default React.memo(Sub);
Copy the code

After the modification, click the number +1 on the parent component to see that the child component is no longer re-rendered.

Custom hooks, logic encapsulation and composition

I like the saying that there are many things in the world, but it is actually made up of more than one hundred elements.

The so-called custom hook is an independent logic body based on several official hooks and obtained through various permutations and combinations + actual business scenarios. Custom hooks are like hooks from the hooks of creativity and imagination: they can write amazing custom hooks.

usage

Here is a simple example:

const useFetchUserInfo = () = > {
    const [isLoading, setIsLoading] = useState(false);
    const [userInfo, setUserInfo] = useState();
    
    const fetchFn = useCallback(async () => {
        setIsLoading(true);
        try {
            const res = awaitxxxx; setUserInfo(res? .data || {}) }finally {
            setIsLoading(false); }});return [isLoading, useInfo, fetchFn];
}
Copy the code

There is no clear “best practice” as to whether custom hooks should return an array or an object, I personally believe that if what is returned is simple enough, it can be returned using an array; If the content is large, the object is returned.

There are two other issues to note:

  1. Custom hooks must be named in useXXX format. The next word must be capitalized. If you try to break this naming rule, the editor will give you an error.

  2. React official hooks can only be used in functional components (capitalized + return ReactElement), or custom hooks, and only be called at the top level.

The principle of

In fact, useState and useEffect are called in the same way as before, and the process is exactly the same whether the list is generated or read. Because we need to understand the nature of custom hooks: it simply wraps several official built-in hook calls into a reusable function, without adding anything new.

advice

In this section, we solved a lot of problems for the project by using Ali open source Ahooks on the project, a very mature, production-level custom hooks library that you should consider using later.