本文经作者受权转载,原文作者:HD Superman,原文链接:一句话解释 useCallback 与 useMemo 的区别 & 作用
背景
最近在重构其余我的项目的代码,发现很多新手写的代码没有做好 hook 内存优化,在解释为什么须要以及 useCallback 和 useMemo 的区别,顺便写下来。
解释
一句话:useCallback 缓存钩子函数,useMemo 缓存返回值(计算结果)。
type DependencyList = ReadonlyArray<any>;
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
一些文章简明扼要解释,其实间接看 typescript 申明就晓得作用了,泛型 T 在 useCallback 中是一个钩子函数,在 useMemo 中是一个返回值。
作用
一个简略计数器 demo 解释全副作用:点击按钮 count 加 1,同时显示这个数是奇数还是偶数
不必 hook 的代码
import React, {FC, useCallback, useMemo, useState} from 'react';
const Index: FC = (props) => {const [count, setCount] = useState(0);
const isEvenNumber = count % 2 === 0;
const onClick = () => setCount(count + 1);
return (
<div>
<div>{count} is {isEvenNumber?'even':'odd'} number</div>
<button onClick={onClick}></button>
</div>
);
};
应用 hook 后的代码
import React, {FC, useCallback, useMemo, useState} from 'react';
const Index: FC = (props) => {const [count, setCount] = useState(0);
const isEvenNumber = useMemo(() => {return count % 2 === 0;}, [count]);
const onClick = useCallback(() => {setCount(count + 1);
}, [count]);
return (
<div>
<div>{count} is {isEvenNumber?'even':'odd'} number</div>
<button onClick={onClick}></button>
</div>
);
};
看起来没有什么区别,甚至应用 hook 后代码还变简单了。这个 demo 比较简单,所有应用 hook 后的优化成果不显著,大部分代码即便应用第一种写法都没有太大区别,用户无感知,但零碎逐渐降级后为了占用更小的内存、更晦涩的应用体验 hook 是必要的。
如果不应用 hook,每次组件 re-render 的时候,都须要从新计算 isEvenNumber 的值,以及 new 一个 onClick 函数,即便每次计算结果没有扭转,也要反复这个节约内存的操作,hook 能够缓存相干后果,防止反复渲染时的有效计算。
useCallback 和 useMemo 的参数都是一个函数加一个依赖数组,依赖没有扭转时间接返回内存中缓存的后果,无需反复计算。简略了解就是 useCallback 缓存事件处理函数,useMemo 缓存二次计算的后果,如下面的点击事件,以及通过 count 值判断奇数偶数的二次计算结果。
实质起因
React 的函数组件是十分好用的货色,相比 class 写法以及 Vue 的对象挂载写法简洁很多,代码测试复用成本低,容易动手,但也带来一些问题,无状态函数很现实 ,但事实有一些计算开销大、组件渲染频繁的场景是 须要状态 的,每次都计算一遍状态(callback 和 二次计算值)无疑很节约内存,函数不像对象(React class 写法或者 Vue 组件写法)能够间接将状态挂载在本身,没有节约内存的问题,要实现相似的成果只能找一个的内存挂载点挂载这些东东,所以有了 useCallback 和 useMemo 这些 hook。