useCallback把匿名回调“存”起来
避免在component render时候声明匿名方法,因为这些匿名方法会被反复重新声明而无法被多次利用,然后容易造成component反复不必要的渲染。
在Class component当中我们通常将回调函数声明为类成员:
class MyComponent extends React.Component { constructor(props) { super(props); this.clickCallback = this.clickCallback.bind(this); } clickCallback() { // ... } render() { return <button onClick={this.clickCallback}>Click Me!</button>; } }
使用useCallback hook就可以避免bind操作:
function MyComponent(props) { const clickCallback = React.useCallback(() => { // ... }, []); return <button onClick={clickCallback}>Click Me!</button>; }
useCallback缓存函数
const fnA = useCallback(fnB, [a])
上面的useCallback会将我们传递给它的函数fnB返回,并且将这个结果缓存;当依赖a变更时,会返回新的函数。既然返回的是函数,我们无法很好的判断返回的函数是否变更,所以我们可以借助ES6新增的数据类型Set来判断,具体如下:
import React, { useState, useCallback } from 'react'; const set = new Set(); export default function Callback() { const [count, setCount] = useState(1); const [val, setVal] = useState(''); const callback = useCallback(() => { console.log(count); }, [count]); set.add(callback); return <div> <h4>{count}</h4> <h4>{set.size}</h4> <div> <button onClick={() => setCount(count + 1)}>+</button> <input value={val} onChange={event => setVal(event.target.value)}/> </div> </div>; }
我们可以看到,每次修改count,set.size就会+1,这说明useCallback依赖变量count,count变更时会返回新的函数;而val变更时,set.size不会变,说明返回的是缓存的旧版本函数。
使用场景:
有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
import React, { useState, useCallback, useEffect } from 'react'; function Parent() { const [count, setCount] = useState(1); const [val, setVal] = useState(''); const callback = useCallback(() => { return count; }, [count]); return <div> <h4>{count}</h4> <Child callback={callback}/> <div> <button onClick={() => setCount(count + 1)}>+</button> <input value={val} onChange={event => setVal(event.target.value)}/> </div> </div>; } function Child({ callback }) { const [count, setCount] = useState(() => callback()); useEffect(() => { setCount(callback()); }, [callback]); return <div> {count} </div> }
不仅是上面的例子,所有依赖本地状态或props来创建函数,需要使用到缓存函数的地方,都是useCallback的应用场景。
useEffect、useMemo、useCallback都是自带闭包的。也就是说,每一次组件的渲染,其都会捕获当前组件函数上下文中的状态(state, props),所以每一次这三种hooks的执行,反映的也都是当前的状态,你无法使用它们来捕获上一次的状态。对于这种情况,我们应该使用ref来访问。