useCallback和useMemo的使用及区别

68 阅读3分钟

useCallBack在什么情况下使用?

在往子组件传入了一个函数并且子组件被React.momo缓存了的时候使用 简单说,React.memo()是通过校验props中的数据是否改变的来决定组件是否需要重新渲染的一种缓存技术,具体点说React.memo()其实是通过校验Props中的数据的内存地址是否改变来决定组件是否重新渲染组件的一种技术。

假设我们往子组件(假设子组件为Child组件)传入一个函数呢?当父组件的其他State与Child组件无关的state)改变的时候。那么,因为状态的改变,父组件需要重新渲染,那被React.memo保护的子组件(Child组件)是否会被重新构建?

示例一

import {useCallBack,memo} from 'react';
/**父组件**/
const Parent = () => {
    const [parentState,setParentState] = useState(0);  //父组件的state
    
    //需要传入子组件的函数
    const toChildFun = () => {
        console.log("需要传入子组件的函数");
        ...
    }
    
    return (<div>
          <Button onClick={() => setParentState(val => val+1)}>
              点击我改变父组件中与Child组件无关的state
          </Button>
          //将父组件的函数传入子组件
          <Child fun={toChildFun}></Child>
    <div>)
}
 
/**被memo保护的子组件**/
const Child = memo(() => {
    consolo.log("我被打印了就说明子组件重新构建了")
    return <div><div>
})
 
复制代码
React.memo检测的是props中数据的栈地址是否改变。而**父组件重新构建的时候,会重新构建父组件中的所有函数**(旧函数销毁,新函数创建,等于更新了函数地址),新的函数地址传入到子组件中被props检测到栈地址更新。也就引发了子组件的重新渲染。

所以,在上面的代码示例里面,子组件是要被重新渲染的。

# 总结
  • useCallBack不要每个函数都包一下,否则就会变成反向优化,useCallBack本身就是需要一定性能的
  • useCallBack并不能阻止函数重新创建,它只能通过依赖决定返回新的函数还是旧的函数,从而在依赖不变的情况下保证函数地址不变
  • useCallBack需要配合React.memo使用

useMemo在什么情况下使用

`useMemo` Hook 可用于防止昂贵的资源密集型函数不必要地运行。

在这个例子中,我们有一个在每次渲染上运行的昂贵函数。

在更改计数或添加待办事项时,您会注意到执行延迟。

实例:

性能不佳的功能。 expensiveCalculation 函数在每次渲染时运行:

        ```
import { useState } from "react";
import ReactDOM from "react-dom/client";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);
  const calculation = expensiveCalculation(count);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
  };

  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Expensive Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const expensiveCalculation = (num) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

**`useMemo`Hook 接受第二个参数来声明依赖项。 昂贵的函数只会在其依赖项发生变化时运行。
    
 > 如果返回一个函数或对象或数组,则使用 useCallback,而当返回一个基元时使用 useMemo

不,这是错误的。`useCallback`主要用于记忆功能。`useMemo`有助于避免昂贵的计算。
 
`useMemo`Hook 接受第二个参数来声明依赖项。 昂贵的函数只会在其依赖项发生变化时运行。
    
 > 如果返回一个函数或对象或数组,则使用 useCallback,而当返回一个基元时使用 useMemo

不,这是错误的。`useCallback`主要用于记忆功能。`useMemo`有助于避免昂贵的计算。**