编辑
2024-01-01
React
00
请注意,本文编写于 325 天前,最后修改于 325 天前,其中某些信息可能已经过时。

目录

基本使用
实现原理
最佳实践
总结

React Hooks有很多种,其中之一就是useMemo,它可以帮助我们优化组件的性能,避免不必要的渲染和计算。本文将介绍useMemo的基本使用,实现原理,最佳实践和一些常见的问题。

基本使用

useMemo接受两个参数:一个创建函数和一个依赖数组。useMemo会返回创建函数的返回值,并且只有当依赖数组中的某个值发生变化时,才会重新执行创建函数。useMemo的基本语法如下:

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

上面的代码中,useMemo会返回computeExpensiveValue(a, b)的结果,并且只有当ab发生变化时,才会重新调用computeExpensiveValue(a, b)。这样可以避免在每次渲染时都执行computeExpensiveValue(a, b),从而提高性能。

useMemo的一个典型的应用场景是,当我们有一个复杂的组件,它接受一个对象作为props,而这个对象是由父组件通过一些计算得到的,我们可以使用useMemo来缓存这个对象,避免在每次父组件渲染时都重新创建这个对象,导致子组件也重新渲染。例如:

js
// 父组件 function Parent({ a, b }) { // 使用useMemo来缓存data对象,只有当a或b变化时,才重新创建data对象 const data = useMemo(() => ({ a, b }), [a, b]); return ( <div> <p>a: {a}</p> <p>b: {b}</p> <Child data={data} /> </div> ); } // 子组件,使用React.memo来避免不必要的渲染 const Child = React.memo(({ data }) => { console.log("Child render"); return <div>{JSON.stringify(data)}</div>; });

上面的代码中,如果我们改变ab的值,父组件会重新渲染,同时也会重新创建data对象,导致子组件也重新渲染。但是如果我们改变其他的状态,比如父组件的cd,父组件会重新渲染,但是data对象不会变化,所以子组件不会重新渲染。这样可以提高子组件的性能,避免不必要的渲染。

实现原理

useMemo的实现原理其实很简单,它就是利用了闭包和缓存的机制,来保存上一次的创建函数和依赖数组,然后在每次渲染时,比较当前的依赖数组和上一次的依赖数组,如果有变化,就重新执行创建函数,否则就返回上一次的缓存值。下面是一个简单的useMemo的实现:

js
// 用一个全局变量来保存上一次的创建函数和依赖数组 let lastCreate; let lastDeps; function useMemo(create, deps) { // 如果没有传入依赖数组,或者依赖数组的长度不一致,就重新执行创建函数 if (!lastDeps || lastDeps.length !== deps.length) { lastCreate = create; lastDeps = deps; return create(); } // 否则,遍历依赖数组,比较每一项是否相等,如果有不相等的,就重新执行创建函数 for (let i = 0; i < deps.length; i++) { if (lastDeps[i] !== deps[i]) { lastCreate = create; lastDeps = deps; return create(); } } // 如果依赖数组都相等,就返回上一次的缓存值 return lastCreate(); }

最佳实践

使用useMemo时,有一些最佳实践和注意事项,我们需要遵守和了解,以免出现错误或者性能问题。下面列举了一些常见的最佳实践:

  • 不要在useMemo中执行有副作用的操作,比如修改状态,发起请求,订阅事件等。useMemo不保证创建函数的执行时机,有可能会被React跳过或者延迟执行。如果需要执行有副作用的操作,应该使用useEffect或者useLayoutEffect。
  • 不要依赖useMemo来保证引用的稳定性,比如用useMemo来返回一个函数或者一个组件。useMemo不保证缓存值的永久有效性,有可能会被React在任何时候清除或者重置。如果需要保证引用的稳定性,应该使用useCallback或者useRef。
  • 不要过度使用useMemo,因为useMemo本身也有一定的开销,比如比较依赖数组,缓存值等。如果创建函数的执行代价不高,或者组件的渲染代价不高,或者组件的渲染频率不高,就没有必要使用useMemo。只有当我们确实遇到了性能问题,才需要使用useMemo来优化。
  • 不要忘记传入依赖数组,或者传入错误的依赖数组。如果不传入依赖数组,useMemo就会在每次渲染时都重新执行创建函数,失去了缓存的意义。如果传入错误的依赖数组,比如漏掉了某个依赖,或者传入了不相关的依赖,就会导致缓存值的不正确或者不必要的更新。我们应该尽量保证依赖数组的完整和准确,可以使用ESLint的react-hooks插件来帮助我们检查依赖数组。

总结

useMemo是一个非常有用的Hook,它可以帮助我们优化组件的性能,避免不必要的渲染和计算。

useMemo的基本用法是,传入一个创建函数和一个依赖数组,返回创建函数的返回值,并且只有当依赖数组中的某个值发生变化时,才会重新执行创建函数。

useMemo的实现原理是,利用闭包和缓存的机制,来保存上一次的创建函数和依赖数组,然后在每次渲染时,比较当前的依赖数组和上一次的依赖数组,如果有变化,就重新执行创建函数,否则就返回上一次的缓存值。

使用useMemo时,我们需要遵守一些最佳实践和注意事项,比如不要在useMemo中执行有副作用的操作,不要依赖useMemo来保证引用的稳定性,不要过度使用useMemo,不要忘记传入依赖数组,或者传入错误的依赖数组。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:CreatorRay

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!