我们知道,react的hook组件再state和props发生改变时,会重新渲染组件,从而导致子组件也会被重新渲染,但有些时候,子组件的props和state值没有变化,子组件就没必要重新渲染,因为如果子组件是一个大型的组件树,这种情况下虚拟DOM(Virtual Dom)的比较明显是很浪费资源的,此时就可以进行优化。在我的上一篇博客中有写到,Memo的作用,可以使用React.memo
函数包裹子组件,这样在传递给子组件的 props
的值没有改变时,子组件就不会重新渲染。
// 父组件
const Father = () => {
const [value, setValue] = useState(0)
const changeValue = () => {
setValue(value => value + 1)
}
return (
<div>
<Child value={value} changeValue={changeValue}>
</div>
)
}
// 子组件
const Child = ({value, changeValue}) => {
console.log('子组件渲染了')
return (
<>
<button onClick={changeValue}>改变数据</>
<p>{value}</p>
</>
)
}
但是当子组件中的props中还存在函数属性changeValue
时,当父组件重新渲染的时候,会生成一个新的函数对象cahngeValue
,从而导致传入子组件的changeValue
对象发生改变,引起子组件重新渲染。
我们是不希望父组件中changeVlaue
函数重新生成的,第一,它在重新渲染时没有什么改变;第二,它重新生成会导致子组件被重新渲染。所以我们可以把这个函数的功能缓存下来,每次使用缓存的函数,就不会导致重新渲染,useCallback
因此而生。
useCallback
缓存的是函数。把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。否则在组件重新渲染的时候,该函数也不会重新生成,而是采用已经缓存的版本,故不会导致重新渲染。
看下面代码解释:
// 会缓存函数状态,函数不会改变,函数中的value值永远也不会改变,是初始值缓存的状态
const changeValue= useCallback(() => {
setValue(value => value + 1)
}, [])
// 会缓存函数状态,在value改变时,改变函数状态,函数中value值也会变
// 注意:如果useCallback中函数使用到了父组件state中的值,一定要放在依赖数组中,比如value
const changeValue= useCallback(() => {
setValue(value => value + 1)
}, [value])
// 不会缓存函数,每次组件重新渲染时都会重新生成一个新的 changeValue 函数对象,引起props变化,渲染子组件
const changeValue = () => {
setValue(value => value + 1)
}
一般情况下,useCallback
函数是配合 memo
函数一起使用的,利用缓存函数使子组件不会重新渲染,如果不使用 React.meomo()
函数,子组件是会重新渲染的。
注意:
1、当依赖数组为空时,函数状态不会改变,是初始值时的状态
2、当依赖数组有值时,当该值变化时,函数状态改变,子组件重新渲染,该值不变化,函数状态不改变,子组件不会渲染
首先要区分 useMemo
和 memo
useMemo 是一个hook
memo是一个高阶组件
useMemo
函数可以缓存已有的计算结果,在组件发生更新重新渲染时,会采用已有的缓存结果,而不是重新进行计算,从而达到性能优化的效果。
如果使用过Vue,就会发现,useMemo
和 Vue
中的 computed
类似。
// computeSum 值计算时比较占用性能的函数
// value, status 为依赖性,当 value 或 status 的值发生改变时,重新渲染的时候,会重新计算computeSum
// 这有助于避免每次重新渲染时都进行高开销的计算
const [value, setValue] = useState(100)
const [status, setStatus] = useState(200)
const computeSum = useMemo(() => {
return value + status
}, [value, status])
注意:
传入 useMemo
的函数,会在渲染期间执行。不要在这个函数内部执行与渲染无关的操作,比如清除副作用。
如果没有提供依赖项数组,useMemo在每次渲染时都会计算新的值
useMemo
和 memo
的优化方式,都是通过缓存的方式进行优化, React.memo
是高阶组件,通过包裹组件,可以让组件在只有 props
发生变化时(默认进行浅比较,第二个传参可以制定比较方式),才会重新渲染组件。