何时使用 useCallback 与 useMemo

何时使用 useCallback 与 useMemo

前言

React 中有许多性能优化的手段,useMemo 与 useCallback 是 hooks 推行后最为常用的两种方法,但是任何优化方案都是有成本的,如果组件都套上 useCallback、Memo,不仅代码可读性会变差,反而会增加性能消耗,还会因为参数的传递问题产生非预期的 bug。不是不推荐使用,必要时用可以达到更好的效果。

useCallback(同useEffect一样,根据[]是否变化来决定是否渲染)

// a, b 参数不变时,memoizedCallback 的引用不变
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemo

// a, b 参数不变时,memoizedValue 的值不变(即 computeExpensiveValue(复杂函数) 不被执行)
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

相同点

无论 useCallback 还是 useMemo 都是通过缓存来优化性能的,只不过 useCallback 缓存的是函数的引用,useMemo 缓存的则是(函数)返回值。两者可以做到等价:

useCallback(fn, deps) === useMemo(() => fn, deps))

//需要注意的是,JS 中函数被视为对象,除非他们引用相等,否则两个对象就算值一样,他们也不是全等的:
const foo1 = () => 'bar';
const foo2 = () => 'bar';
const foo1Ref = foo1;
console.log(foo1 === foo2) // false
console.log(foo1 === foo1Ref) // true

区别

useCallback 与 useMemo 返回的内容不同。
useCallback 会返回一个未执行的函数,而 useMemo 则返回执行完函数后的值。

const foo = () => 'bar';

const testCallback = useCallback(foo, []);
const testMemo = useMemo(foo, []);

console.log(testCallback); // foo() {}
console.log(testMemo); // bar
console.log(testCallback()); // bar
console.log(testMemo()); // TypeError: testMemo is not a function

何时使用

在使用函数式组件时,常常会产生组件的重渲染,而组件的重渲染又会带来函数的引用改变或值的重计算.
useMemo 适合用于大量数据运算的场景,如:

const value = React.useMemo(() => {
  return Array(100000).fill('').map(v => v);
}, [a]);

useCallback 则适用于与 memo 搭配,减少由函数操作带来的重渲染,比如下面这个例子:

import React, { useState } from "react";

// 子组件
const Child = ({ handleChildClick }) => {
  console.log("<=== Child render");
  return <button onClick={handleChildClick}>handleChildClick</button>;
};

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);

  const handleChildClick = () => {
    console.log("Click Child");
  };

  const handleParentClick = () => {
    console.log("Click Parent");
    setCount(count => count + 1);
  };

  return (
    <div>
      Count: {count}
      <hr />
      <button onClick={handleParentClick}>handleParentClick</button>
      <Child handleChildClick={handleChildClick} />
    </div>
  );
};

export default Parent;

在不加任何操作的情况下,每次点击 handleParentClick 的按钮,都会打印出 Child render 的信息:

<=== Child render // init

click Parent 
<=== Child render 
click Parent 
<=== Child render

尝试给 handleChildClick 函数套上 useCallback:

  const handleChildClick = useCallback(() => {
    console.log("Click Child");
  }, []);
//发现打印结果还是一样

再给 Child 套一层 memo:

const Child = memo(({ handleChildClick }) => {
  console.log("<=== Child render");
  return <button onClick={handleChildClick}>handleChildClick</button>;
});

此时符合预期:点击父组件不会打印"<=== Child render"

useMemo在什么场景下使用呢?

见参考文章

原理:

首先,将 Child 组件使用 memo 包裹,以达到 React.PureComponent 的效果,即能够对 props 进行浅比较,但是父组件因为 count 的改变,handleChildClick 会不断刷新引用,只有给这个函数套上 useCallback 进行缓存后,memo 的比较才能起作用,最终使得 Child 组件不被重复渲染,性能得到提升。
参考文章:

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值