useCallback 概述
1、useCallback
是在React 中多次渲染缓存函数的 Hook,返回一个函数的 memoized
的值;
2、如果多次传入的依赖项不变,那么多次定义的时候,返回的值是相同的,防止频繁触发更新;
3、多应用在 父组件为函数式组件,子组件也为函数式组件,并且子组件被 React.memo()
包裹着;
4、主要用于性能优化,即使不适用useCallback,代码也要能正常运行;
useCallback 基本用法
写法
let memoizedCallback = useCallback(fn, dependencies)
第一个参数:fn 是想要缓存的函数,可以接收任何参数并返回任何值;React 中会在初始化时候调用,而不是渲染时候调用;
当执行下一次渲染时候,如何出入的依赖值 dependencies
相同,则会返回相同的函数 memoizedCallback
;
相反 若依赖值 dependencies 不同
,则会返回新的函数 memoizedCallback,但是 React 不会主动去调用 memoizedCallback函数,需要开发者自己决定什么时候
执行调用函数;
第二个参数:
dependencies
:是否要更新 fn 的所有响应式值的一个列表,可以传入空数组:[]
;
响应式值包括 props、state
,和所有在你组件内部直接声明的变量和函数。
useCallback 示例
1、为什么要使用 useCallback
当我们传入相同值的时候,不管是对象类型,还是基本类型,都不希望子组件进行更新渲染;
在不需要大量渲染的时候,性能还可以,但是当数据量大的时候,若相同的数据也触发子组件渲染,则会出现性能问题;
2、在不使用 useCallback()
的时候
每次触发 handleAdd
事件时候,都会渲染子组件 ChildA
// 父组件
import { useCallback, useState} from 'react'
import ChildA from './childA'
export default function MyCallBack() {const [useInfo, setUseInfo] = useState({name: 'Andy',age: 18})const myCallback = () => {console.log('==useCallback==')return useInfo.name}const handleAdd = () => {setUseInfo({name: 'Andy',age: 18})}return (<div><h3>This is a MyCallBack demo .---{count}</h3><button onClick={handleAdd}>add</button><hr /><ChildA onAddCount={myCallback} ></ChildA></div>)
}
// 子组件
import React, { memo } from 'react'const ChildA = memo(({onAddCount}) => {console.log('==ChildA 组件更新了=', count)return (<div><h3>This is a ChildA demo .</h3><button onClick={onAddCount}>子组件</button><hr /></div>)
})
当我们点击 add
按钮时候,发现页面打印 “==ChildA 组件更新了=
”,说明传入相同的数据时候,会触发子组件渲染;如图所示
3、使用 useCallback 时候
import { useCallback,useState} from 'react'
import ChildA from './childA'
export default function MyCallBack() {console.log('===父组件callback==')const [count, setCount] = useState(0)const [useInfo, setUseInfo] = useState({name: 'Andy',age: 18})const myCallback = useCallback(() => {console.log('==useCallback==')return useInfo.name}, [useInfo.name])const handleAdd = () => {setUseInfo({name: 'Andy',age: 18})}return (<div><h3>This is a MyCallBack demo .---{count}</h3><button onClick={handleAdd}>add</button><hr /><ChildA onAddCount={myCallback} ></ChildA></div>)
}
// 子组件
import { memo } from 'react'
// eslint-disable-next-line react/display-name
const ChildA = memo(({useInfo, count, onAddCount}) => {console.log('==ChildA 组件更新了=', count)const handleChangeName = () =>{setName('Tom')}return (<div><h3>This is a ChildA demo .</h3><h4>{count}</h4><h4>姓名:{useInfo?.name || '--'}</h4><h4>年龄:{useInfo?.age || '--'}</h4><button onClick={onAddCount}>子组件</button><hr /></div>)
})
export default ChildA
在点击 add
按钮 更新相同数据时候,只有父组件渲染,子组件不会再渲染
;如图:
总结
若要实现 传入相同数据时候,只更新当前组件,而子组件不进行渲染,需使用 useCallback() 和 memo 来处理;