useMemo与useCallback
useMemo与useCallback
useMemo
和useCallback
都可缓存函数的引用或值,从更细的角度来说useMemo
则返回一个缓存的值,useCallback
是返回一个缓存函数的引用。
useMemo
useMemo
的TS
定义可以看出,范型T
在useMemo
中是一个返回的值类型。
type DependencyList = ReadonlyArray<any>;
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
下面是useMemo
的简单示例,在a
和b
的变量值不变的情况下,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
在每次渲染时都会计算新的值。
eslint
的eslint-plugin-react-hooks
中的exhaustive-deps
规则可以在添加错误依赖时发出警告并给出修复建议。
相比较于useEffect
看起来和Vue
的Watch
很像,但是思想方面是不同的,Vue
是监听值的变化而React
是用以处理副作用。在useMemo
方面就和Vue
的computed
非常类似了,同样都属于缓存依赖项的计算结果,当然在实现上是完全不同的。
useCallback
useCallback
的TS
定义可以看出,范型T
在useCallback
中是一个返回的函数类型。
type DependencyList = ReadonlyArray<any>;
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
下面是useCallback
的简单示例,在a
和b
的变量值不变的情况下,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
的语法糖。
eslint
的eslint-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} />;
}
最后
关于useMemo
与useCallback
是否值得尽量多用,私认为并不应该这么做,如果在性能优化方面非常有效,值得在每个依赖或者函数都值得使用useMemo
与useCallback
的话,React
可以干脆将其作为默认的功能,又可以减少用户使用Hooks
的心智负担,又可以减少使用Hooks
的包裹让代码更加简洁,可是React
并没有这么做,实际上这仍然是一个权衡的问题,权衡性能优化的点,取一个折衷,具体来说就是你需要评估你组件re-render
的次数和代价,React.memo
、useMemo
与useCallback
这些缓存机制也是有代价的,需要做好平衡,不能盲目的多用这类缓存优化方案,比起盲目的进行各种细微的优化,分析清楚性能问题出现的原因才能真正的解决问题。
每日一题
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的更多相关文章
- useMemo、useCallback简单理解
1.useMemo.useCallback都是使参数(函数)不会因为其他不想关的参数变化而重新渲染. (1)useMemo const memoDom = useMemo(() => { ret ...
- React中useMemo与useCallback的区别
useMemo 把"创建"函数和依赖项数组作为参数传⼊入useMemo,它仅会在某个依赖项改变时才重新计算memoized 值.这种优化有助于避免在每次渲染时都进⾏行行⾼高开销的计 ...
- useMemo优化React Hooks程序性能(九)
useMemo主要用来解决使用React hooks产生的无用渲染的性能问题.使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是 ...
- React Hooks: useCallback理解
useCallback把匿名回调“存”起来 避免在component render时候声明匿名方法,因为这些匿名方法会被反复重新声明而无法被多次利用,然后容易造成component反复不必要的渲染. ...
- React Hooks 深入系列 —— 设计模式
本文是 React Hooks 深入系列的后续.此篇详细介绍了 Hooks 相对 class 的优势所在, 并介绍了相关 api 的设计思想, 同时对 Hooks 如何对齐 class 的生命周期钩子 ...
- 如何构建自己的 react hooks
我们组的前端妹子在组内分享时谈到了 react 的钩子,趁此机会我也对我所理解的内容进行下总结,方便更多的同学了解.在 React 的 v16.8.0 版本里添加了 hooks 的这种新的 API,我 ...
- React Hooks用法大全
前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...
- React 新特性学习
1 context 2 contextType 3 lazy 4 suspense 5 memo 6 hooks 7 effect hooks =========== 1 Context 提供了一种方 ...
- React Hook挖坑
React Hook挖坑 如果已经使用过 Hook,相信你一定回不去了,这种用函数的方式去编写有状态组件简直太爽啦. 如果还没使用过 Hook,那你要赶紧升级你的 React(v16.8+),投入 H ...
- React Hook~部分实用钩子
useCompareEffect /** * useCompareEffect * useEffect只是普通的浅比较,这里做了深比较 * useEffect的依赖是否相同,相同不触发 */ impo ...
随机推荐
- 【收集】Tool
序 链接 备注 1 WinMerge - You will see the difference- 开源windows比对工具 2 AykutSarac/jsoncrack.com: Seamless ...
- java - 正确关闭流
package stream; import java.io.*; public class FileReaderTest { public static void main(String[] arg ...
- bootstrap : 解决使图片全屏显示有空白边距的问题
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8 ...
- win11不改时区,安装 android 子系统,运行android app
win11开启虚拟机平台 下载安卓子系统 打开https://store.rg-adguard.net , 在URL中输入:https://www.microsoft.com/store/produc ...
- Oracle 不同字符集复合索引长度验证
Oracle 不同字符集复合索引长度验证 背景 前段时间同事找到一个参数, 可以解决Oracle的char和byte 模式存储超长的问题. 很大程度上解决了研发修改SQL的工作量. 但是发现在某些字符 ...
- JVM大页内存的学习与使用
JVM大页内存的学习与使用 原理和背景 操作系统是计算机的重要组成部分. 现代的操作系统一般都采用 段页式内存管理. 段一般是为了管理和权限 页主要是为了虚拟内存和物理内存的映射. 分页管理可以让物理 ...
- [转帖]JVM中年轻代里的对象什么情况下进入老年代?以及老年代垃圾回收算法-标记整理算法
1.躲过15次GC之后进入老年代 系统刚启动时,创建的各种各样的对象,都是分配在年轻代里. 随着慢慢系统跑着跑着,年轻代满了,就会出发Minor GC ,可能1%的少量存活对像转移到空着的Surviv ...
- [转帖]Prometheus-使用python开发exporter
exporter有很多,但想要特定需求的话,还需自行开发.在这里使用python写一个exporter,用于监控/root下的目录数量. 开发exporter需要使用prometheus_client ...
- [转帖]《Linux性能优化实战》笔记(四)—— CPU 使用率
一. 节拍率与CPU时间 前一篇说到,Linux 作为一个多任务操作系统,将每个 CPU 的时间划分为很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成多任务同时运行的错觉. 为了维护 CPU ...
- [转帖]《Linux性能优化实战》笔记(十七)—— Linux网络基础与性能指标
一. 网络模型 1. OSI 网络模型(七层) 为了解决网络互联中异构设备的兼容性问题,并解耦复杂的网络包处理流程,OSI 模型把网络互联的框架分为七层,每个层负责不同的功能.其中, 应用层,负责为应 ...