useCallback和useMemo区别

5,883 阅读3分钟

useCallback

  • 使用对象:函数
  • 使用条件:父组件传给子组件的函数
  • 优点: useCallback包裹的函数,相当于对函数做了缓存,当父组件重新渲染时,函数不会重新定义==>子组件不会重新渲染
  • 注意:依赖项数组不会作为参数传给回调函数。虽然从概念上来说它表现为:所有回调函数中引用的值都应该出现在依赖项数组中。
  • 使用场景:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新
  • useCallback不能滥用,否则只会消耗性能
  • 真正有助于性能改善的,有 2 种场景
    • 函数定义时需要进行大量运算, 这种场景极少
    • 需要比较引用的场景,useEffect,又或者是配合React.Memo使用
  • 同样的场景,如果该组件一定只会渲染一次,那么使用useCallback就完全没有必要。
  • 总结:
    • 利用闭包缓存上次结果
    • 成本:额外的缓存,与比较逻辑
    • 不是绝对的优化,而是一种成本的交换,并非使用所有场景

例子:

const { useCallback, useState, useEffect } = React
const set = new Set();
const Child = ({callback}) =>{
  const [count,setCount] = useState(0);
  useEffect(()=>{
    console.log("09")
    setCount(callback())
  },[callback]) //当callback更新时执行callback函数,得到parent组件最新的count

  return <div>
    child count:{count}
  </div>
}
const App = () => {
  const [count, setCount] = useState(1);
  const [val, setVal] = useState('');

  const callback = useCallback(() => {
    console.log(count, 8);
    return count
  }, [count]); //count更新时执行

  set.add(callback); //借助ES6新增的数据类型Set来判断当依赖count变更时是否返回新的函数
  console.log(set, 9);

  return (
    <div>
      <h4>parent count:{count}</h4>
      <h4>set size:{set.size}</h4>
      <div>
        <button onClick={() => setCount(count + 1)}>+</button>
        <input value={val} onChange={event => setVal(event.target.value)}/>
      </div>

      <Child callback={callback} />
    </div>
  )
}

useMemo

  • useMemo:允许通过记住上一个结果的方式在多次渲染的之间缓存计算结果,使得控制具体子节点何时更新变得更容易
  • 使用对象:变量
  • 使用条件:父组件传给子组件的变量  
  • 优点:useMemo包裹的变量,相当于对变量做了缓存,当父组件重新渲染时,变量不会改变==》子组件不会重渲染
  • useMemo执行的几种情况:
    1. 首次渲染
    2. 非首次渲染
      • 依赖发生改变
      • 为空数组时

举个例子

  • 现在我们有个Tree组件,他会渲染一个很耗性能的大组件ExpensiveTree

image.png

  • 由于AppContext中包含很多与theme无关的state,导致每次其他无关的state更新,Tree都会重新render,进而ExpensiveTree组件也重新render。

  • useMemo就能派上用场

image.png

  • 即使AppContext改变导致Tree反复render,ExpensiveTree也只会在theme改变后render。

useCallback和useMemo的区别

  • 相同点:
    1. useCallback 和 useMemo 参数相同,第一个参数是函数,第二个参数是依赖项的数组。
    2. useMemo、useCallback 都是使参数(函数)不会因为其他不相关的参数变化而重新渲染。
    3. 与 useEffect 类似,[] 内可以放入你改变数值就重新渲染参数(函数)的对象。如果 [] 为空就是只渲染一次,之后都不会渲染
  • 区别:
    • 主要区别是 React.useMemo 将调用 fn 函数并返回其结果,而 React.useCallback 将返回 fn 函数而不调用它。