useCallBack
需要思考的点
当你定义一个函数式组件的时候,这个组件的内部有一些内部的函数,那么这个组件更新渲染的时候,内部定义的一些函数是不是重新声明创建了一个新的?
想来验证这个问题!那么就要去渲染前后的引用(指针/函数所在的内存地址)是不是真的发生了变化!如何验证是否真的发生了变化呢!
我们可以把函数当作 props 传入 memo 子组件!我们知道memo组件的特点!只有props发生变化,memo才会重新渲染!
那么我们可以得出一个判断条件:
如果,当父组件重新渲染的时候,memo子组件如果也重新渲染的话!那么说明,函数也重新声明创建了一个新的!反之,则没有!
import "./styles.css";
import React from "react";
const ChildComp1 = React.memo(({ handleSetCount }) => {
console.log("child1 render ");
return <div onClick={handleSetCount}>ChildComp</div>;
});
const ChildComp2 = React.memo(({ handleSetCount }) => {
console.log("child2 render ");
return <div onClick={handleSetCount}>ChildComp</div>;
});
export default function App() {
const [count1, setCount1] = React.useState(0);
const [count2, setCount2] = React.useState(0);
const handleSetCount1 = () => {
setCount1(count1 + 1);
};
const handleSetCount2 = () => {
setCount2(count1 + 1);
};
return (
<div className="App">
<p>count1{count1}</p>
<p>count2{count2}</p>
<ChildComp1 handleSetCount={handleSetCount1} />
<ChildComp2 handleSetCount={handleSetCount2} />
</div>
);
}
上面的代码中!我们定义了两个 memo 子组件! ChildComp1 和 ChildComp2 在App组件中,使用这两个子组件!
并且声明了两个函数去改变state,然后把这两个函数分别传递给了两个子组件!当这两个函数任意一个被调用的时候,因为state发生了变化,那么势必导致App组件会重新渲染!那么如果此时子组件 console.log(“xxx”) 执行的话!也就说明了,父组件内部的函数也都重新声明创建了新的函数!
下面我们来验证一下!
从上面的执行结果来看!我们父组件渲染的时候,两个memo子组件也重新渲染了!
说明子组件的props发生了渲染,那么就证明了此时App内部的相关函数是重新声明了的!
这时 useCallback 就派上了用场!
useCallback
返回一个 memoized 回调函数。
把内联回调函数及依赖项数组作为参数传入
useCallback
,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如
shouldComponentUpdate
)的子组件时,它将非常有用。
语法:[useCallback(fn, deps)]
useCallback 会缓存函数 包括函数内部的一些状态!
我们把上面的例子中的函数变成一个 useCallback函数!
const handleSetCount1 = useCallback(() => {
setCount1(count1 + 1);
}, []);
const handleSetCount2 = useCallback(() => {
setCount2(count2 + 1);
}, []);
这次再看,memo子组件有没有因为父组件的重新渲染而渲染!
可以看到当父组件重新渲染的时候!子组件并没有重新渲染了!(没有输出 xx render!)但是后面的多次点击调用函数的时候发现父组件也没有更新了!这是为什么呢?
这就是我们说的 [同样会缓存内部的一些状态!]
两个函数的 count1,count2 的值始终为 0,第一次调用后,父组件的 state 因为 0 -> 1 的变化发生了渲染!
但是因为缓存函数内部的 count1,count2 的值始终为 0,所以后面的调用父组件的state始终是 1 -> 1 不会发生渲染
这个时候呢!就用到了callback第二个参数[...deps]
,这个参数表示你可以传入一些依赖项!当依赖项发生变化的时候!函数才会更新!
const handleSetCount1 = useCallback(() => {
setCount1(count1 + 1);
}, [count1]);
const handleSetCount2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
这样的话呢~当 countx 改变的时候,只会更新自己的函数!只会重新渲染相关的memo子组件!函数内部的state也会同步更新,也解决了父组件状态更新的问题!
一般 memo 和 useCallback结合起来使用,memo用来控制内部state,useCallback控制外部传入props函数造成多余更新渲染影响!
评论区