The usage scenarios and in-depth interpretation of useCallback of React hooks

The two main points of the article

1. Not every function needs to use useCallBack!

2. Under what circumstances is useCallBack used?

useCallBack is not required for every function

Seeing this, some pen pals are about to ask three consecutive questions.

1. Why not wrap every function with useCallBack?

2. Isn't useCallBack a cache tool?

3. Wouldn't caching every function improve performance better?

useCallBack is a caching tool yes. But in fact he can't prevent the function from recreating the build.

for example

 
 
//Com组件
const Com =  () => {
 
    //示例1包裹了useCallBack的函数
    const fun1 = useCallBack(() => {
        console.log('示例一函数');
        ...
    },[])
    
     //示例2没有包裹useCallBack的函数
    const fun2 = () => {
        console.log('示例二函数');
        ...
    }
    return <div></div>
}
 
复制代码

Look at the components with the structure above. The Com component contains two functions, fun1 and fun2.

Do you think that when the Com component is re-rendered, only the fun2 (function without useCallBack) function will be rebuilt, and the fun1 (function with useCallBack) function will not be rebuilt.

In fact, the function wrapped by useCallBack will also be reconstructed and passed in as the actual parameter of the useCallBack function.

The essential work of useCallBack is not to prevent function creation when the dependency is unchanged, but to return the old function address instead of the new function address when the dependency is unchanged. Regardless of whether useCallBack is used or not, it cannot prevent the function from being recreated when the component is rendered! !

Every function called by useCallBack will be added to the internal management queue of useCallBack. And when we use useCallBack a lot, there will be a lot of functions in the management queue. When any component that uses useCallBack is re-rendered, it needs to facilitate all the managed functions inside useCallBack to find the function that needs to verify whether the dependency has changed. and verify.

In the above process, finding the specified function requires performance, and verification also requires performance. Therefore, the abuse of useCallBack not only cannot prevent the function from being rebuilt, but also adds the two functions of "finding the specified function and verifying whether the dependency has changed", which adds unnecessary burden to the project.

When is useCallBack used?

Used when passing a function to a child component and the child component is cached by React.momo


As mentioned in the previous section, the function of useCallBack is not to prevent function creation, but to return the old function address (keep the address unchanged) when the dependency remains unchanged.

React .memo(), is a caching technique. The pen pals who can see here don't need me to explain in detail what React.memo does.

Simply put, React.memo() is a caching technique that determines whether a component needs to be re-rendered by checking whether the data in props has changed. Specifically, React.memo() actually checks the data in Props A technique that determines whether a component re-renders if the memory address changes.

Suppose we pass a function to the child component (assuming the child component is a Child component)? When the other State of the parent component ( state unrelated to the Child component ) changes. So, because the parent component needs to be re-rendered because of the state change, will the child component (Child component) protected by React.memo be rebuilt?

On this issue, let me give you a chestnut. There are the following ↓ code snippets

Code example one

import {useCallBack,memo} from 'react';
/**父组件**/
const Parent = () => {
    const [parentState,setParentState] = useState(0);  //父组件的state
    
    //需要传入子组件的函数
    const toChildFun = () => {
        console.log("需要传入子组件的函数");
        ...
    }
    
    return (<div>
          <Button onClick={() => setParentState(val => val+1)}>
              点击我改变父组件中与Child组件无关的state
          </Button>
          //将父组件的函数传入子组件
          <Child fun={toChildFun}></Child>
    <div>)
}
 
/**被memo保护的子组件**/
const Child = memo(() => {
    consolo.log("我被打印了就说明子组件重新构建了")
    return <div><div>
})
 
复制代码

Question: When I click the Button in the parent component to change the state in the parent component. Whether child components will re-render. At first glance, the parentState variable is changed, which has nothing to do with the subcomponent. The subcomponent is still protected by React.memo, and it seems that it will not be re-rendered. But the problem here is that if you want to pass in another variable, it will work. But what is passed in is a function, no, it won't work. will re-render.

React.memo detects whether the stack address of the data in props has changed. When the parent component is rebuilt, all functions in the parent component will be rebuilt (the old function is destroyed, and the new function is created, which is equivalent to updating the function address). The new function address is passed into the child component and is detected by props to update the stack address. . It also triggers the re-rendering of the child component.

So, in the above code example, the child component is to be re-rendered.

So how can the child component not be re-rendered? Here comes the correct way to use useCallBack.

Use useCallBack to wrap the function that needs to be passed into the subcomponent. In that case, when the parent component re-renders, the function in the child component will return the old function address because it is protected by useCallBack, and the child component will not detect the address change, and will not re-select the rendering.

Still the above code example, we perform the following optimizations.

Code example two

import {useCallBack,memo} from 'react';
/**父组件**/
const Parent = () => {
    const [parentState,setParentState] = useState(0);  //父组件的state
    
    //需要传入子组件的函数
    //只有这里和上一个示例不一样!!

    const toChildFun = useCallBack(() => {
        console.log("需要传入子组件的函数");
        ...
    },[])
    
    return (<div>
          <Button onClick={() => setParentState(val => val+1)}>
              点击我改变父组件中与Child组件无关的state
          </Button>
          //将父组件的函数传入子组件
          <Child fun={toChildFun}></Child>
    <div>)
}
 
/**被memo保护的子组件**/
const Child = memo(() => {
    consolo.log("我被打印了就说明子组件重新构建了")
    return <div><div>
})
 
复制代码

This way, child components won't be re-rendered.

The difference between Code Example 1 and Code Example 2 is whether the function of the subcomponent passed in (toChildFun function) is protected by useCallBack.

We only need to use useCallBack to protect the function (toChildFun function) passed into the child component in the parent component to ensure that it will not return a new memory address when it is not necessary.

Summarize

  • useCallBack does not need to wrap every function, otherwise it will become reverse optimization, useCallBack itself requires a certain performance
  • useCallBack does not prevent the function from being recreated, it can only decide whether to return the new function or the old function through the dependency, so as to ensure that the function address remains unchanged when the dependency remains unchanged
  • useCallBack needs to be used with React.memo

Guess you like

Origin blog.csdn.net/hyupeng1006/article/details/127754586