JAVASCRIPT:如何用React.useMemo()函数?

719 阅读3分钟

如何用React.useMemo()进行记忆?

React组件不时地要进行昂贵的计算。例如,给定一个大的雇员列表和一个搜索查询,组件应该通过查询来过滤雇员的名字。

在这种情况下,只要小心,你可以尝试使用备忘录技术来提高组件的性能。

在这篇文章中,我将描述如何以及何时使用useMemo() React挂钩。

1.useMemo() 钩子

useMemo() 是一个内置的React钩子,接受2个参数--一个计算结果的函数computedepedencies 数组。

 const memoizedResult = useMemo(compute, dependencies); 

在初始渲染期间,useMemo(compute, dependencies) 调用compute ,将计算结果备忘化,并将其返回给组件。

如果在接下来的渲染过程中,依赖关系没有变化,那么useMemo() 就不会调用 compute ,而是返回备忘的值。

但是如果在重新渲染的过程中,依赖关系发生了变化,那么useMemo() 就会调用 compute ,记忆新的值,并将其返回。

这就是useMemo() 钩子的本质。

如果你的计算回调使用道具或状态值,那么一定要把这些值作为依赖关系来表示。

const memoizedResult = useMemo(() => {
  return expensiveFunction(propA, propB);
}, [propA, propB]);

现在让我们在一个例子中看看useMemo() 是如何工作的。

2.useMemo()- 一个例子

一个组件<CalculateFactorial /> ,计算一个引入到输入字段的数字的阶乘。

下面是<CalculateFactorial /> 组件的一个可能的实现。

import { useState } from 'react';
export function CalculateFactorial() {
  const [number, setNumber] = useState(1);
  const [inc, setInc] = useState(0);
  const factorial = factorialOf(number);
  const onChange = event => {
    setNumber(Number(event.target.value));
  };
  const onClick = () => setInc(i => i + 1);
  
  return (
    <div>
      Factorial of 
      <input type="number" value={number} onChange={onChange} />
      is {factorial}
      <button onClick={onClick}>Re-render</button>
    </div>
  );
}
function factorialOf(n) {
  console.log('factorialOf(n) called!');
  return n <= 0 ? 1 : n * factorialOf(n - 1);
}

每当你改变输入值时,阶乘的计算factorialOf(n)'factorialOf(n) called!' ,并记录到控制台。

在另一边,每次你点击Re-render按钮,inc 状态值就会被更新。更新inc 状态值会触发<CalculateFactorial /> 重新渲染。但是,作为次要的影响,在重新渲染期间,阶乘又被重新计算了--'factorialOf(n) called!' 被记录到控制台。

当组件重新渲染时,你怎样才能将阶乘计算备忘化?欢迎使用useMemo() 钩子!

通过使用useMemo(() => factorialOf(number), [number]) ,而不是简单的factorialOf(number) ,React对阶乘的计算进行了备忘。

让我们改进<CalculateFactorial /> ,将阶乘计算备忘化。

import { useState, useMemo } from 'react';
export function CalculateFactorial() {
  const [number, setNumber] = useState(1);
  const [inc, setInc] = useState(0);
  const factorial = useMemo(() => factorialOf(number), [number]);
  const onChange = event => {
    setNumber(Number(event.target.value));
  };
  const onClick = () => setInc(i => i + 1);
  
  return (
    <div>
      Factorial of 
      <input type="number" value={number} onChange={onChange} />
      is {factorial}
      <button onClick={onClick}>Re-render</button>
    </div>
  );
}
function factorialOf(n) {
  console.log('factorialOf(n) called!');
  return n <= 0 ? 1 : n * factorialOf(n - 1);
}

打开演示。每次你改变数字的值时,'factorialOf(n) called!' 被记录到控制台。这是预期的。

然而,如果你点击Re-render按钮,'factorialOf(n) called!' 不会被记录到控制台,因为useMemo(() => factorialOf(number), [number]) 会返回备忘的阶乘计算。很好!

3.useMemo()vsuseCallback()

useCallback(),与useMemo() 相比,是一个更专门的钩子,它将回调进行备忘。

import { useCallback } from 'react';
function MyComponent({ prop }) {
  const callback = () => {
    return 'Result';
  };
  const memoizedCallback = useCallback(callback, [prop]);
  
  return <ChildComponent callback={memoizedCallback} />;
}

在上面的例子中,只要prop 依赖关系相同,useCallback(() => {...}, [prop]) 就会返回同一个函数实例。

你可以用同样的方式使用useMemo() 来记忆回调。

import { useMemo } from 'react';
function MyComponent({ prop }) {
  const callback = () => {
    return 'Result';
  };
  const memoizedCallback = useMemo(() => callback, [prop]);
  
  return <ChildComponent callback={memoizedCallback} />;
}

4.谨慎使用记忆化

虽然useMemo() 可以提高组件的性能,但你必须确保对有钩子和没有钩子的组件进行剖析。只有在这之后,才能得出结论,膜化是否值得。

当备忘化使用不当时,它可能会损害性能。

5.5.结论

useMemo(() => computation(a, b), [a, b]) 钩子可以让你对昂贵的计算进行记忆。考虑到相同的 依赖关系,[a, b]一旦备忘化,钩子就要返回备忘化的值,而不调用 computation(a, b)