MST

星途 面试题库

面试题:React中useCallback与useMemo在性能优化场景中的区别及应用

在React性能优化方面,useCallback和useMemo都起着重要作用。请详细说明它们在功能、使用场景上的区别,并举例说明何时使用useCallback更合适,何时使用useMemo更合适。
27.1万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

功能区别

  1. useCallback:返回一个 memoized 回调函数。它的主要作用是确保在依赖项没有变化时,返回的回调函数引用保持不变。这有助于防止不必要的重新渲染,特别是在将回调函数作为 prop 传递给子组件时。
  2. useMemo:返回一个 memoized 值。它会在其依赖项发生变化时重新计算 memoized 值。如果依赖项没有变化,就返回上一次计算的值,从而避免重复计算开销较大的操作。

使用场景区别

  1. useCallback
    • 防止子组件不必要渲染:当把回调函数传递给子组件,而子组件依赖于回调函数的引用不变时使用。例如,子组件使用 React.memo 进行包裹,并且依赖于传入的回调函数来决定是否重新渲染。如果回调函数引用改变,即使子组件的其他 props 没有变化,也会触发重新渲染。
    • 事件处理函数:在处理频繁触发的事件,如 onClickonChange 等,且该事件处理函数依赖一些 state 或 props 时,使用 useCallback 可以避免每次渲染都创建新的函数。
  2. useMemo
    • 计算开销大的操作:当有复杂的计算逻辑,且该计算结果在依赖项未改变时不需要重新计算,如复杂的数组排序、过滤,对象的深度比较等。通过 useMemo 缓存计算结果,减少不必要的计算开销。
    • 创建对象或数组:当需要在组件内部创建对象或数组,且依赖项未改变时不需要重新创建,使用 useMemo 可以避免每次渲染都创建新的对象或数组。

示例

  1. useCallback示例
import React, { useState, useCallback } from 'react';

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent rendered');
  return <button onClick={onClick}>Click me</button>;
});

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

export default ParentComponent;

在这个例子中,ChildComponent 使用了 React.memo,如果 handleClick 函数不使用 useCallback,每次 ParentComponent 渲染时都会创建新的 handleClick 函数,导致 ChildComponent 不必要的重新渲染。使用 useCallback 后,只有 count 变化时 handleClick 的引用才会改变,从而避免 ChildComponent 不必要的重新渲染。

  1. useMemo示例
import React, { useState, useMemo } from 'react';

const ExpensiveCalculation = ({ a, b }) => {
  console.log('ExpensiveCalculation performed');
  return a + b;
};

const ParentComponent = () => {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState(10);

  const result = useMemo(() => {
    return ExpensiveCalculation({ a: count, b: value });
  }, [count, value]);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Value: {value}</p>
      <p>Result: {result}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setValue(value + 1)}>Increment Value</button>
    </div>
  );
};

export default ParentComponent;

在这个例子中,ExpensiveCalculation 模拟一个开销较大的计算函数。使用 useMemo 后,只有 countvalue 变化时才会重新计算 result,否则返回上一次计算的值,避免了不必要的计算。