1. 主页 > 好文章

React Hooks缓存函数技巧:避免重复渲染提升性能

哎,有没有发现你的React应用越用越卡?明明功能都实现了,怎么就时不时卡顿两下?我刚开始学React的时候也碰到过这个问题——页面像老式拖拉机一样吭哧吭哧地刷新,后来才发现是掉进了??重复渲染??的坑里。今天就和大家聊聊新手最头疼的性能问题,特别是那个让小白们集体破防的终极疑问:??为什么我的组件像打地鼠一样不停重刷???

(偷偷告诉你,这个解决方法可比“新手如何快速涨粉”实用多了)


一、你的React可能在疯狂做无用功

举个真实场景:某天我写了个点赞按钮组件

javascript复制
function LikeButton() {
  const handleClick = () => { /* 点赞逻辑 */ }
  return <button onClick={handleClick}>点赞button>
}  

看起来没问题对吧?但当你把这个按钮放在动态列表中时,每次其他按钮被点击,所有LikeButton组件都会??整个重刷一遍??,像集体抽风似的。这就是典型的不必要渲染,根源就出在——每次渲染都创建??全新的handleClick函数??!

这时候肯定有人问:??那怎么让函数老老实实待着别乱跑???答案马上揭晓。


二、两个救命锦囊:useCallback和useMemo

React给我们准备了两把钥匙:

  1. ??useCallback——专治函数多动症??
    把上面的例子改造下:

    javascript复制
    const handleClick = useCallback(() => {
      /* 点赞逻辑 */
    }, []);  

    这个空数组[]就像给函数上了锁,意思是说“只要组件还活着,这个函数永远不变”。

  2. ??useMemo——对付计算型暴走??
    遇到需要复杂计算的场景:

    javascript复制
    const result = useMemo(() => 
      heavyCalculation(input), 
    [input]);

    只有当input值变化时才重新计算,这点和Vue的计算属性有点像。


三、新手最常踩的三大雷区

最近有个学员发来的代码让我哭笑不得:

javascript复制
useCallback(() => {
  console.log(count);
}, [count]);  // ? 正确操作
   
useCallback(() => {
  console.log(count);
}, []);       // ? count永远定格在初始值

??问题来了:依赖项到底该怎么写???
记住这个土办法:闭包里的变量全都要放进依赖数组!就像查户口本一样,函数里用到的state、props一个都不能漏。

再举个反面教材:

javascript复制
// ? 把整个对象当参数缓存
const config = useMemo(() => ({
  color: 'red',
  size: 'large'
}), []);  

这样写的后果是——每次config都是??全新对象??,缓存了个寂寞!应该这样改:

javascript复制
const config = useMemo(() => 
  ({ color: 'red', size: 'large' }), 
[]);  

四、灵魂拷问环节

??Q:是不是每个函数都要缓存啊???
A:大错特错!举个反例:

javascript复制
const formatDate = useCallback((date) => 
  date.toLocaleString(), 
[]  

这种纯工具函数缓存就是脱裤子放屁——完全没有必要。

??Q:用useMemo能替代useCallback吗???
看看这个对比表:

场景useCallbackuseMemo
缓存类型函数任意值
适用场景事件处理复杂计算
内存消耗较低较高

所以结论是:??别拿菜刀裁衣服——工具要用对地方??。


??小编观点??:
用了五年React,最大的教训就是别把缓存当灵丹妙药。去年做的一个后台系统,在useCallback里套了二十多层依赖,最后追踪bug差点把自己绕晕。以我的血泪史告诉大家——能用React.memo解决的别上useCallback,需不需要缓存就像炒菜放盐,加得刚刚好才是真功夫。

本文由嘻道妙招独家原创,未经允许,严禁转载