什么时候使用两个钩子--useCallback和useMemo?

95 阅读3分钟

React中的每一个状态变化都会导致重新渲染,这可能会对应用程序产生严重的影响。主要是当组件尺寸增加时。useCallbackuseMemo 钩子被用来提高React应用程序的性能。

useCallback和useMemo做的是同一件事吗?

虽然两者都是用于性能优化,但对于开发者来说,他们可能会对他们的使用情况感到困惑。 因此,让我们彻底了解他们。

在深入了解它们的区别之前,让我们先了解一下它们被使用的原因。

  • 如上所述,这两个钩子都是用于性能优化的。
  • 如果我们的应用程序经常进行重新渲染,那么根据使用情况,我们可以使用其中之一。

让我们看看它们在语法上有什么不同。

useCallback(()=>{
  doSomething();
}, [dependencies]);

useMemo(()=>{
  doSomething();
}, [dependencies]);

正如我们所看到的,除了命名之外,它们在语法上没有任何区别。 两个钩子都接受一个函数和一个依赖数组。

useMemo与useCallback有什么不同?

useMemo和useCallback钩子的主要区别是,useMemo ,返回记忆化的值,而useCallback ,返回记忆化的函数。

还在困惑吗? 没问题。我们将通过一个例子来了解两者的区别。

假设我们有一个父组件。

import { React, useState } from 'react';
import ChildComponent from './ChildComponent'

function App() {
  const [num, setNum] = useState(0);

  return(
    <div>
      <h1>{num}</h1>
      <ChildComponent />
      <button onClick={() => setNum(num + 1)}> Addition </button>
    </div>
  );
}

和一个子组件

import { React } from 'react';

function ChildComponent() {
  console.log("child component rendered")
  
  return(
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

export default ChildComponent;

我们可以观察到,每次我们点击Addition 按钮时,都会打印控制台语句--child component renderedimage.png 尽管状态变化只发生在父组件中,但子组件却在不必要地重新渲染。

让我们来看看如何避免这种情况。

了解useMemo

在这种情况下,useMemo 可以给我们提供备忘的结果。只需用useMemo 包装来调用子组件。

所以,父组件会看起来像。

import { React, useState, useMemo } from 'react';
import ChildComponent from './ChildComponent'

function App() {
  const [num, setNum] = useState(0);

  const getChildComp = useMemo(() => <ChildComponent />, []);

  return(
    <div>
      <h1>{num}</h1>
      {getChildComp}
      <button onClick={() => setNum(num + 1)}> Addition </button>
    </div>
  );
}

我们可以观察到在这里,当我们点击Addition 按钮时,控制台语句不会被打印出来。

image.png

因此,在这种情况下,useMemo 帮助我们提高了性能。

现在,让我们看看useCallback 是如何工作的。

了解useCallback

只要在父组件中创建一个函数,并将其传递给子组件。 这样,父组件就会看起来像。

import { React, useState } from 'react';
import ChildComponent from './ChildComponent'

function App() {
  const [num, setNum] = useState(0);

  const handleUpdateNum = () => {
    //some code
  };

  const getChildComp = useMemo(
    () => <ChildComponent handleUpdateNum={handleUpdateNum} />,
    [handleUpdateNum]
  );

  return(
    <div>
      <h1>{num}</h1>
      {getChildComp}
      <button onClick={() => setNum(num + 1)}> Addition </button>
    </div>
  );
}

现在,如果我们试图点击Addition 按钮,我们可以在控制台看到child component rendered 。所以,在这种情况下,useMemo 不会有帮助。

image.png

因为每当父组件被重新渲染时,handleUpdateNum 函数就会被重新创建。

这时,useCallback 出现了,它为我们返回记忆化的函数。

让我们用useCallback 钩子包裹handleUpdateNum函数,看看结果。因此,父组件的代码将看起来像这样。

import { React, useState, useCallback } from 'react';
import ChildComponent from './ChildComponent'

function App() {
  const [num, setNum] = useState(0);

  const handleUpdateNum = useCallback(() => {
    //some code
  }, []);

  const getChildComp = useMemo(
    () => <ChildComponent handleUpdateNum={handleUpdateNum} />,
    [handleUpdateNum]
  );
  
  return(
    <div>
      <h1>{num}</h1>
      {getChildComp}
      <button onClick={() => setNum(num + 1)}> Addition </button>
    </div>
  );
}

现在我们可以看到,即使在父组件的状态发生变化后,子组件也没有重新渲染。

image.png

总结

虽然我们已经探讨了useCallbackuseMemo ,但我们可以根据自己的需求来使用这些钩子。 只需将这些钩子视为React中的微调。 它不能修复写得不好的代码,但如果一切都符合要求,这些钩子将提供其额外的好处。