React Hooks - useCallback讲解与使用场景
useCallback
接收一个函数和一个依赖项数组作为参数,然后返回一个 memoized 版本的函数。如果依赖项数组中的任何一个依赖项发生更改,则 useCallback
会返回一个新的函数。如果依赖项数组不变,则 useCallback
将返回上次 memoized 的函数。
使用 useCallback
的好处是可以避免在每次重新渲染时都创建新的回调函数,从而减少不必要的组件重新渲染。尤其是当传递给子组件的回调函数发生更改时,这种优化可以显著提高性能。
前言
useCallback
理解需要涉及到函数组件每次更新会形成一个新的闭包的更新过程与浅比较的知识点,如果不太了解建议看看以前的文章
一个东西出来肯定是为了解决某个问题,我们先来看看问题。
父组件
import { useState } from "react";
import Children from "./children";
export default function App() {
const [count, setCount] = useState(0);
const [chartData, setChartData] = useState([
{ year: "1991", value: 3 },
{ year: "1992", value: 4 },
{ year: "1993", value: 3.5 },
{ year: "1994", value: 5 },
{ year: "1995", value: 4.9 },
{ year: "1996", value: 6 },
{ year: "1997", value: 7 },
{ year: "1998", value: 9 },
{ year: "1999", value: 13 },
]);
const chartClick = (plot: any) => {
plot.on("legend-item:click", (...args: any) => {
console.log("图表的点击事情,可参考文档添加其它事件", args);
});
};
// 优化 看完下面的问题所在解决方法在看
// const chartClick = useCallBack((plot: any) => {
// plot.on("legend-item:click", (...args: any) => {
// console.log("图表的点击事情,可参考文档添加其它事件", args);
// });
// }, [chartData])
function handleClick() {
setCount(count + 1);
}
return (
<div style={{ position: "fixed", top: 100, right: 100 }}>
Count: {count}
<br />
<button onClick={handleClick}>count ++ </button>
<Children chartData={chartData} bindReady={chartClick} />
</div>
);
}
子组件
import { Line } from "@ant-design/charts";
export default function Children(props: any) {
const { chartData, bindReady } = props;
const config = {
data: chartData,
height: 400,
xField: "year",
yField: "value",
point: {
size: 5,
shape: "diamond",
},
};
return (
<div style={{ width: 200, height: 200 }}>
<Line onReady={bindReady} {...config} />
</div>
);
}
- 代码执行效果:
问题
我在通常在使用图表的时候都会把他封装成一个组件传递对应的参数让它显示对应的图表就行了。在这里我们把父组件中的 count 和 图表 看成两个整体
互不相干。
但在图中我们可以看到 当 count 更新的时候,明明图表的数据没有变动但它重新渲染了一下(图中的图表闪动)。这性能和展示效果都不是很好。
理想下:我们只修改了 count 的值那么只更新 count 就好了, Children 组件不需要给我重新渲染
解决
解决的方法很简单,我们把 chartClick 方法用 useCallback 包一下添加一个依赖就好了
。
原因
这是因为 React 每一次更新都会是一个新的闭包 ,它里面的变量啊函数啊都会重新创建。你虽然看着值没有变,但是地址都改变了。props 和 state 变动时 React 都会进行浅比较
。
在代码中 你虽然没有改变 Children 组件接收的 bindReady 和 chartData 属性,但你 setCount 改变值时,导致 App 组件重新渲染形成了新的闭包。 chartClick 方法跟着就会重新创建
,那么地址也就改变了。 React 浅比较时发现两次地址不一样就会去更新
。
最后
有些爱动脑经的小伙伴就发现了。既然每次更新函数都会重新创建,那我给每个函数声明都包一层 useCallBack 不就好了。
实则不然只会适得其反。不是每个函数都适合,全部加了话有时候会引起一些奇怪的问题,对于性能提升来说也没多大帮助
。
啥时候加呢? 如果你这个函数里面的状态很多,调用非常频繁,或者文中这个函数需要传递给子组件时 就可以使用。
useCallback 第二个参数
只有监测到第二个参数改变时他才会更新函数。有很多小伙伴不知道这个依赖改写啥。其实这个依赖就看你这个函数中使用了那些状态,这些就是 useCallback 的依赖。当然有一些不是,就像文章中的依赖是 chartData 。
转载自:https://juejin.cn/post/7229573641371942971