先看一下官方对于useCallback的定义:
useCallback是一个允许你在多次渲染中缓存函数的React Hook
这句话包含了俩个因素:
- useCallback的作用:缓存函数
- useCallback的使用场景:多次渲染下
什么叫多次渲染呢?组件嵌套的时候会存在多次渲染,如下:
import React, { useState, useCallback, memo} from 'react';
const Child = (props) => {
console.log('子组件渲染了')
const {childCount} = props;
return (
<div>
<h1>子组件:{childCount}</h1>
</div>
)
}
const Parent = () => {
console.log('父组件渲染了')
const [count, setCount] = useState(0);
const [childCount, setChildCOunt] = useState(0)
return (
<div>
<h1>父组件:{count}</h1>
<button onClick={()=>{setCount(count+1)}}>点击父组件值加一</button>
<Child childCount={childCount}/>
</div>
)
}
export default Parent;
通过这个例子,我们能看到,当父组件中的state发生变化的时候,子组件也会触发渲染,即使变化的state并没有作为props传入子组件
如何解决这个问题呢?React.memo登场,只需要将Child用memo包裹,如下:
const Child = memo((props) => {
console.log('子组件渲染了')
const {childCount} = props;
return (
<div>
<h1>子组件:{childCount}</h1>
</div>
)
})
memo 允许你的组件在 props 没有改变的情况下跳过重新渲染(注意默认是浅比较)
但是如果传入子组件的props中包含了一个函数的话,会发生什么事呢?
import React, { useState, useCallback, memo} from 'react';
const Child = memo((props) => {
console.log('子组件渲染了')
const {childCount,watchChildCount} = props;
//执行传入的watchChildCount
watchChildCount()
return (
<div>
<h1>子组件:{childCount}</h1>
</div>
)
})
const Parent = () => {
console.log('父组件渲染了')
const [count, setCount] = useState(0);
const [childCount, setChildCount] = useState(0)
//给子组件传入一个函数
const watchChildCount = () => {
console.log(childCount,'打印childCount')
}
return (
<div>
<h1>父组件:{count}</h1>
<button onClick={()=>{setCount(count+1)}}>点击父组件值加一</button>
<Child childCount={childCount} watchChildCount={watchChildCount}/>
<button onClick={() => {setChildCount(childCount+1)}}>点击子组件值加一</button>
</div>
)
}
export default Parent;
此时你再点击 父组件值加一 的这个按钮,会发现,即使子组件已经被memo包裹了,子组件还是触发了渲染
这是因为,组件每次渲染的时候,其中的函数都会被重新创建,React.memo使用的是浅比较,所以看似作为props的fn没有发生变化,其实它已经是一个新的fn了
如何解决这个问题呢?useCallback登场
const watchChildCount = useCallback(() => {
console.log(childCount,'打印childCount')
},[childCount])
useCallback能够将函数缓存起来,只依赖于给定的状态值来确定该函数是否需要重新创建,避免父组件每次更新都创建的这种情况
以上代码都是即粘即用的,感兴趣的可以自己cra一个react项目,去试一下