共计 1588 个字符,预计需要花费 4 分钟才能阅读完成。
本文记录个人对 React 的 hook 中 useEffect,useCallback,useMemo 的个人理解。
三者的定义:
useEffect(didUpdate, deps);
const memoizedCallback = useCallback(() => {doSomething(params);
}, deps);
const memoizedValue = useMemo(() => computeExpensiveValue(params), deps);
三者的第二个 deps 是一个数组,表示依赖的参数列表,当依赖列表中任一参数变化时,则重新执行前面的函数。如 deps = [a,b]
, 表示 a 或者 b 发生变化则执行前面的函数(注意对象和值的变化方式不同)。
当deps=[]
时,表示只会在组件第一次渲染成功之后执行一次。
useEffect
didUpdate 是一个包含命令式、且可能有副作用代码的函数。
didUpdate 是组件渲染成功且 deps 依赖参数发生变化时执行的函数,也就是说 useEffect 会执行 didUpdate
。
didUpdate 可以没有返回值,只是执行 didUpdate 的内容。
didUpdate 当它有返回值时,返回值必须是个可执行的函数,目的是用于清除 didUpdate 执行过程中产生的订阅或者计时器等资源。同时如果 didUpdate 多次触发,则在每次重新执行前都会先执行返回的可执行函数。(官方称之为清除 effect)
如下:
useEffect(() => {const timer = setInterval(()=>{console.log('effect')
}, 10*1000);
return () => {
// 清除定时器
clearInterval(timer);
};
});
因此对于 useEffect 来说,didUpdate 时 deps 有变化时就是执行的,didUpdate 若有返回值时,则在下次执行 didUpdate 时会先执行其返回的函数。
useCallback
返回一个 memoized 回调函数,我的理解即返回一个函数的句柄,等同于函数的变量,因此你可以使用 memoizedCallback()进行执行该函数或者传递给事件和子组件,这里可以推荐绝大多数事件或者子组件的方法使用 useCallback,避免组件更新重复渲染。
因此 useCallback 中的 doSomething 并不会在定义时就执行,而是需要手动调用返回的 memoizedCallback 才是真的执行。简单理解为 useCallback 定义了一个函数,仅在 deps 发生变化时重新定义该函数,否则该函数的变量不会变化,事件和子组件内容也就不用重新绑定或者渲染。
useMemo
返回一个 memoized 值,useMemo 函数每当 deps 发生变化时都会调用执行 computeExpensiveValue 的内容,这是与 useCallback 最大的不同,useCallback 不执行 doSomething 的内容,只是重新刷新函数句柄。
因此在官方上有这样的一个等式:useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
这应该看懂了吧。deps 发生变化时,useCallback 返回的是一个可执行 fn
的句柄,而 useMemo 则是执行 ()=>fn
,但是因为返回的是 fn 函数,因此当调用这两种时其实执行的是相同的fn
函数内容。
总结
我自身对三者的理解是基于 hook 定义时:首参是否执行和各自返回内容的作用与差异进行理解,如果本文无法准确描述清楚,建议你也可以从这两方面进行入手分析三者之间的区别和用途。
当然这样的解释和理解可能无法去解释什么情况用 useEffect、useCallback、useMemo。但是作为开发者而言,理解 hook 其定义的差异,亦可理解其不同的用途目的,也就能区分清楚三者各自应在什么情况下进行使用。