【React Hooks】useMemo/useCallback 用法

前言:记忆函数

在实践开发中,有一种优化手段叫做记忆函数。

  • 例如:我们封装一个方法来实现计算从1到某个整数的总和

function summation(target: number) {
  let sum = 0;
  for(let i = 1; i <= target; i++) {
    sum += i;
  }
  return sum;
}
  • 这个时候,我们思考一个问题,当我们重复调用summation(100)时,函数内部的循环计算是不是有点冗余?
  • 因为传入的参数一样,得到的结果必定也是一样,因此如果传入的参数一致,是不是可以不用再重复计算直接用上次的计算结果返回呢?
  • 这时候可以利用闭包能够实现我们的目的

// 初始化一个非正常数字,用于缓存上一次的计算结果
let preTarget = -1;
let memoSum = 0;

export function memoSummation(target: number) {
  // 如果传入的参数与上一次一样,直接换回缓存结果
  if (preTarget > 0 && preTarget === target) {
    return memoSum;
  }

  console.log('我出现,就表示重新计算了一次');
  // 缓存本次传入的参数
  preTarget = target;
  let sum = 0;
  for (let i = 1; i <= target; i++) {
    sum += i;
  }
  // 缓存本次的计算结果
  memoSum = sum;
  return sum;
}
  • 上面的这就是记忆函数。
  • 记忆函数利用闭包,在确保返回结果一定正确的情况下,减少了重复冗余的计算过程。
  • 这是我们试图利用记忆函数去优化我们代码的目的所在。

两者用法和区别

// fn : 函数,deps:依赖数组
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
/*
只推荐第三种情况下去使用他们:
	1. 无依赖数组时,相当于每次渲染时都会执行fn
	2. 依赖数组为空,相当于只执行一次fn
	3. 依赖数据非空,依赖项改变时,会重新调用fn
*/

useMemo缓存计算结果。它接收两个参数:

  • 第一个参数为计算过程(回调函数,必须返回一个结果),
  • 第二个参数是依赖项(数组),当依赖项中某一个发生变化,结果将会重新计算。
const memoizedValue = React.useMemo(
	()=>computeExpensiveValue(a,b),
	[a,b]
);

  • useCallback的使用几乎与useMemo一样
  • 不过useCallback缓存的是一个函数体,当依赖项中的一项发现变化,函数体会重新创建。
const memoizeCallback = useCallback(
	()=>{
		doSomething(a,b);
	},
	[a,b],
);

示例:

import React, { useMemo, useState, useCallback } from 'react';
import { Button } from 'antd-mobile';

export default function App() {
  const [target, setTarget] = useState(0);
  const [other, setOther] = useState(0)

  const sum = useMemo(() => {
    console.log('重新计算一次');
    let _sum = 0;
    for (let i = 1; i <= target; i++) {
      _sum += i;
    }
    return _sum;
  }, [target]);

  const inputChange = useCallback((e) => {
    console.log(e.target.value);
  }, []);

  return (
    <div style={{ width: '200px', margin: 'auto' }}>
      <input type="text" onChange={inputChange} />
      <div style={{ width: '80px', margin: '100px auto', fontSize: '40px' }}>{target} {sum}</div>
      <Button onClick={() => setTarget(target + 1)}>递增</Button>
      <Button onClick={() => setTarget(target - 1)}>递减</Button>

      <div style={{ width: '80px', margin: '100px auto', fontSize: '20px' }}>干扰项 {other}</div>
      <Button onClick={() => setOther(other + 1)}>递增</Button>
      <Button onClick={() => setOther(other - 1)}>递减</Button>
    </div>
  )
}

注意使用和误区

useMemo/useCallback的使用非常简单,不过我们需要思考一个问题,使用他们一定能够达到优化的目的吗?

React的学习经常容易陷入过度优化的误区。一些人在得知shouldComponentUpdate能够优化性能,恨不得每个组件都要用一下,不用就感觉自己的组件有问题。useMemo/useCallback也是一样。

明白了记忆函数的原理,我们应该知道,记忆函数并非完全没有代价,我们需要创建闭包,占用更多的内存,用以解决计算上的冗余。

而当我们使用useMemo/useCallback时,由于新增了对于闭包的使用,新增了对于依赖项的比较逻辑,因此,盲目使用它们,甚至可能会让你的组件变得更慢。

大多数情况下,这样的交换,并不划算,或者赚得不多。你的组件可能并不需要使用useMemo/useCallback来优化。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗不甘坠落的流星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值