useCallback钩子函数

135 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

useCallback简单来说就是返回一个函数,只有在依赖项发生变化的时候才会更新(返回一个新的函数)。

场景:设置一个父组件,里面包含它的子组件,并且给这个子组件传父组件拥有的值。我们利用memo,React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。本质是生成一个性能优化的组件,过滤掉状态未改变时造成子组件不要的声明。

如下列代码,只给子组件传递了count一个属性值,当点击按钮改变父组件的其他三个属性的时候,子组件不会重新渲染,因为用memo包裹了起来。

子组件:

function Child(props){
            const {count} = props
            console.log("renderChild");
            return(
                <div>    
                    <div>child :{count}</div>
                </div>
            )
        }

父组件如下:

 function App(){
 const [count,setCount] = useState(0)
 const [id,setId] = useState(1)
 const [sex,setSex] = useState("小红")
 const [age,setAge] = useState(19)
  return(
     <div>
     <p>App id :{id}</p>
     <p>App age :{age}</p>
     <p>App sex :{sex}</p>
     <button onClick={()=>{setId(id+1)}}>改变用户id</button>
     <button onClick={()=>{setAge(age+1)}}>改变用户age</button>
     <button onClick={()=>{setSex("小刚")}}>改变用户sex</button>
      <button onClick={()=>{setCount(count+1)}}>改变count</button>
     <MemoChild  count={count}  />
     </div>
     )
 }
 

但是,当给子组件传递一个函数时,如果count没有改变只更改其他三个属性的话,那memo性能优化是不好用的。

function Child(props){
            const {count,handleClick} = props
            console.log("renderChild");
            return(
                <div>    
                    <div>child :{count}</div>
                    <button onClick={
                        ()=>{
                            handleClick() //点击调用父组件中方法
                        }
                    }>count+1</button>
                </div>
            )
}
 const MemoChild = memo(Child)
         function App(){
            console.log("renderApp");
            const [id,setId] = useState(1)
            const [age,setAge] = useState(19)
            const [sex,setSex] = useState("小红")
            const [count,setCount] = useState(0)
            //把函数传给子组件调用
            const handleClick=()=>{
                setCount(count+1);
            }
            return(
                <div>
                <p>App id :{id}</p>
                <p>App age :{age}</p>
                <p>App sex :{sex}</p>
                <button onClick={()=>{setId(id+1)}}>改变用户id</button>
                <button onClick={()=>{setAge(age+1)}}>改变用户age</button>
                <button onClick={()=>{setSex("小刚")}}>改变用户sex</button>
                <MemoChild  count={count} handleClick={handleClick} /> //传过去
                </div>
            )
        }

同样使用了memo组件,但是却由于是使用传递过来的函数改变count值,这个memo就失效了。

可知,纯状态改变时,memo有用,但函数改变时,memo就失效了。

于是为了改善这个缺陷,我们使用useCallback钩子函数,它可以将这个点击函数包裹起来,可以使当count改变时才可以重新渲染这个子组件,其他三个状态改变无效。

  const handleClick=useCallback(()=>{
                setCount(count+1);
            })

注意:

useCallback钩子函数有几种使用情况。

  1. 当没有数组时
 const handleClick=useCallback(()=>{
                setCount(count+1);
            })//什么都不写,那么默认只要改变一次父子组件任意就渲染一次子组件

image.png

  1. 当数组占位,却为空时
 const handleClick=useCallback(()=>{
                setCount(count+1);
            },[])//注意看,这里写了[]但是没有赋值什么的

这种情况在useEffect中被视为componentDidMount一种情况,只在加载完页面时调用一次。

当点击其他改变其他三个属性的时候。

image.png

当点击改变count时,第一次点击时,子组件进行声明,再次点击,就不改变了。

image.png

  1. 当数组中明确了需要依赖哪一个属性改变时:
const handleClick=useCallback(()=>{
                setCount(count+1);
            },[count])

只有当count发生改变的时候才会声明子组件,其他三个属性改变都不会影响到子组件的声明。