useMemo与useCallback

useMemouseCallback都可缓存函数的引用或值,从更细的角度来说useMemo则返回一个缓存的值,useCallback是返回一个缓存函数的引用。

useMemo

useMemoTS定义可以看出,范型TuseMemo中是一个返回的值类型。

type DependencyList = ReadonlyArray<any>;

function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;

下面是useMemo的简单示例,在ab的变量值不变的情况下,memoizedValue的值不变,在此时useMemo函数的第一个参数也就是computeExpensiveValue函数不会被执行,从而达到节省计算量的目的。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

把创建函数factory: () => T和依赖项数组deps: DependencyList | undefined作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算memoized 值,这种优化有助于避免在每次渲染时都进行高开销的计算,例如上文的computeExpensiveValue是需要一个大量计算的函数时,useMemo有助于减少性能开销,以防止Js太多次长时间运行计算导致页面无响应。

此外,传入useMemo的函数会在渲染期间执行,所以不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect的适用范畴,而不是useMemo。如果没有提供依赖项数组,useMemo在每次渲染时都会计算新的值。

eslinteslint-plugin-react-hooks中的exhaustive-deps规则可以在添加错误依赖时发出警告并给出修复建议。

相比较于useEffect看起来和VueWatch很像,但是思想方面是不同的,Vue是监听值的变化而React是用以处理副作用。在useMemo方面就和Vuecomputed非常类似了,同样都属于缓存依赖项的计算结果,当然在实现上是完全不同的。

useCallback

useCallbackTS定义可以看出,范型TuseCallback中是一个返回的函数类型。

type DependencyList = ReadonlyArray<any>;

function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

下面是useCallback的简单示例,在ab的变量值不变的情况下,memoizedCallback的函数引用不变,在此时useCallback函数的第一个参数不会被重新定义,即引用的依旧是原函数,从而达到性能优化的目的。

const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);

把内联回调函数callback: T及依赖项数组deps: DependencyList作为参数传入 useCallback,它将返回该回调函数的memoized版本,该回调函数仅在某个依赖项改变时才会更新,将回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。此外,useCallback(fn, deps)相当于useMemo(() => fn, deps),由此useCallback可以看作useMemo的语法糖。

eslinteslint-plugin-react-hooks中的exhaustive-deps规则可以在添加错误依赖时发出警告并给出修复建议。

useCallback的应用方面,在这里引用一下 @松松 给出的例子,一般Js上创建一个函数需要的时间并不至于要缓存的程度,那为什么要专门给缓存函数的创建做一个语法糖呢,这就跟React.memo有关系了。React.memo的默认第二参数是浅对比shallow compare上次渲染的props和这次渲染的props,如果你的组件的props中包含一个回调函数,并且这个函数是在父组件渲染的过程中创建的(见下例),那么每次父组件(下例中的<MyComponent />)渲染时,React是认为你的子组件(下例中的<Button />)props是有变化的,不管你是否对这个子组件用了React.memo,都无法阻止重复渲染。这时就只能用useCallback来缓存这个回调函数,才会让React(或者说Js)认为这个prop和上次是相同的。

// 下面三种方法都会在MyComponent渲染的过程中重新创建这个回调函数
// 这样都会引起Button的重新渲染 因为Button的props变化了
function MyComponent() {
return <Button onClick={() => doWhatever()} />;
} function MyComponent() {
const handleClick = () => doWhatever();
return <Button onClick={handleClick} />;
} function MyComponent() {
function handleClick(){
doWhatever();
}
return <Button onClick={handleClick} />;
} // 只有使用useCallback, 才会导致即使MyComponent渲染,也不重新创建一个新的回调函数
// 这样就不会引发Button的重新渲染 因为Button的props没变
function MyComponent() {
const handleClick = React.useCallBack(() => doWhatever(), []);
return <Button onClick={handleClick} />;
}

最后

关于useMemouseCallback是否值得尽量多用,私认为并不应该这么做,如果在性能优化方面非常有效,值得在每个依赖或者函数都值得使用useMemouseCallback的话,React可以干脆将其作为默认的功能,又可以减少用户使用Hooks的心智负担,又可以减少使用Hooks的包裹让代码更加简洁,可是React并没有这么做,实际上这仍然是一个权衡的问题,权衡性能优化的点,取一个折衷,具体来说就是你需要评估你组件re-render 的次数和代价,React.memouseMemouseCallback这些缓存机制也是有代价的,需要做好平衡,不能盲目的多用这类缓存优化方案,比起盲目的进行各种细微的优化,分析清楚性能问题出现的原因才能真正的解决问题。

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://www.zhihu.com/question/428921970
https://www.zhihu.com/question/390974405
https://juejin.cn/post/6844904032113278990
https://juejin.cn/post/6844904001998176263
https://segmentfault.com/a/1190000039405417
https://www.infoq.cn/article/mm5btiwipppnpjhjqgtr
https://zh-hans.reactjs.org/docs/hooks-reference.html

useMemo与useCallback的更多相关文章

  1. useMemo、useCallback简单理解

    1.useMemo.useCallback都是使参数(函数)不会因为其他不想关的参数变化而重新渲染. (1)useMemo const memoDom = useMemo(() => { ret ...

  2. React中useMemo与useCallback的区别

    useMemo 把"创建"函数和依赖项数组作为参数传⼊入useMemo,它仅会在某个依赖项改变时才重新计算memoized 值.这种优化有助于避免在每次渲染时都进⾏行行⾼高开销的计 ...

  3. useMemo优化React Hooks程序性能(九)

    useMemo主要用来解决使用React hooks产生的无用渲染的性能问题.使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是 ...

  4. React Hooks: useCallback理解

    useCallback把匿名回调“存”起来 避免在component render时候声明匿名方法,因为这些匿名方法会被反复重新声明而无法被多次利用,然后容易造成component反复不必要的渲染. ...

  5. React Hooks 深入系列 —— 设计模式

    本文是 React Hooks 深入系列的后续.此篇详细介绍了 Hooks 相对 class 的优势所在, 并介绍了相关 api 的设计思想, 同时对 Hooks 如何对齐 class 的生命周期钩子 ...

  6. 如何构建自己的 react hooks

    我们组的前端妹子在组内分享时谈到了 react 的钩子,趁此机会我也对我所理解的内容进行下总结,方便更多的同学了解.在 React 的 v16.8.0 版本里添加了 hooks 的这种新的 API,我 ...

  7. React Hooks用法大全

    前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...

  8. React 新特性学习

    1 context 2 contextType 3 lazy 4 suspense 5 memo 6 hooks 7 effect hooks =========== 1 Context 提供了一种方 ...

  9. React Hook挖坑

    React Hook挖坑 如果已经使用过 Hook,相信你一定回不去了,这种用函数的方式去编写有状态组件简直太爽啦. 如果还没使用过 Hook,那你要赶紧升级你的 React(v16.8+),投入 H ...

  10. React Hook~部分实用钩子

    useCompareEffect /** * useCompareEffect * useEffect只是普通的浅比较,这里做了深比较 * useEffect的依赖是否相同,相同不触发 */ impo ...

随机推荐

  1. 问题--flask无法发邮件,无法登录

    1.问题 早上测试项目的时候,一直无法正确发送邮件,无法接收,但是查不出原因是什么 2.解决 改变了一下思路,去登录了不需要邮件验证码的用户,发现错误 这个错误提示是一个数据库连接错误,表明应用程序无 ...

  2. [转帖]Oracle优化案例:vfs_cache_pressure和min_free_kbytes解决RMAN挂起问题

    https://www.modb.pro/db/34028 环境: Oracle 11gr2 + dataguard 512GB内存 + 128核cpu + 高性能存储服务器 uname -an Li ...

  3. [粘贴]TiDB Lightning 断点续传

    https://www.bookstack.cn/read/tidb-6.1-zh/tidb-lightning-tidb-lightning-checkpoints.md 大量的数据导入一般耗时数小 ...

  4. [转帖]SSL数字证书分类DV/OV/EV

    SSL证书的分类主要是通过下面两个维度进行分类: 1.根据验证模式分类 根据CA机构对申请者的身份审核范围分为:DV证书.OV证书.EV证书. 1.1.DV证书(域名证书) DV(Domain Val ...

  5. [转帖]数据库篇-MySql架构介绍

    https://zhuanlan.zhihu.com/p/147161770 公众号-坚持原创,码字不易.加微信 : touzinv 关注分享,手有余香~ 本篇咱们也来聊聊mysql物理和逻辑架构,还 ...

  6. Springboot 使用nacos鉴权的简单步骤

    Springboot 使用nacos鉴权的简单步骤 背景 前端时间nacos爆出了漏洞. 因为他的默认token固定,容易被利用. 具体的问题为: QVD-2023-6271 漏洞描述:开源服务管理平 ...

  7. Sysbench的简单学习-编译与安装

    sysbench的简单学习-编译与安装 摘要 github上面获取一下最新版本 https://github.com/akopytov/sysbench 注意现在 2023.2.17 最新版是 sys ...

  8. echasrts定义折线图legend的样式-优化

    option = { title: { text: '折线图堆叠' }, tooltip: { trigger: 'axis' }, //定义折线图legend的形状哈 legend: { itemW ...

  9. NextJs 与 Tailwind 入门开发笔记

    前言 距离上次更新已经过去好久了,之前我在 StarBlog 博客2023年底更新一览的文章里说要使用 Next.js 来重构博客前端,最近也确实用 next.js 做了两个小项目,一个是单点认证项目 ...

  10. ClickHouse(06)ClickHouse建表语句DDL详细解析

    目录 当前服务器上创建表(单节点) 语法形式 使用显式架构 从相同结构的表复制创建 从表函数创建 从选择查询创建 分布式集群创建表 临时表 分区表 创建表语句关键字解析 空值或非空修饰符 默认值表达式 ...