React useCallback钩子使用指南与性能优化技巧

React.js近年来广受欢迎,这已经不是什么秘密了。它现在是许多互联网上最杰出的参与者(包括Facebook和WhatsApp)的首选JavaScript库。其兴起的主要原因之一是在16.8版本中引入了钩子,这一创新极大地改变了React的开发模式。React钩子允许开发者在不编写类组件的情况下利用React函数,而带有钩子的功能组件已成为开发人员使用React的首选结构。在这篇博文中,我们将深入探讨一个特定的钩子——useCallback,因为它涉及函数式编程的一个基本部分,即记忆化。通过本文,您将确切地知道如何以及何时使用useCallback钩子,并充分利用其性能增强功能。

### 什么是Memoization?

Memoization是一种优化技术,当一个复杂的函数存储它的输出以便下次使用相同的输入调用它时,它能够避免重复计算。这类似于缓存,但在本地级别上操作。通过记忆化,函数可以跳过任何复杂的计算并更快地返回输出,因为它已经计算过了。这种技术对内存分配和性能产生重大影响,而useCallback钩子的主要目的就是解决这种性能压力。

### React的useCallback与useMemo

在React中,useCallback与另一个名为useMemo的钩子经常协同工作。虽然两者都涉及记忆化,但它们的应用场景有所不同。useCallback返回一个记忆化的函数,而useMemo返回一个记忆化的值。这意味着useMemo适用于存储计算结果,而useCallback则适用于缓存函数以便后续调用。这两个钩子都会在它们的依赖项之一(例如状态或道具)发生变化时才重新计算。

以下是一个示例代码,展示了useMemo和useCallback的区别:

“`javascript
import { useMemo, useCallback } from ‘react’;
const values = [3, 9, 6, 4, 2, 1];
const memoizedValue = useMemo(() => values.sort(), [values]);
const memoizedFunction = useCallback(() => values.sort(), [values]);
“`

在这个例子中,`memoizedValue`会返回一个排序后的数组,只要`values`变量保持不变,它就会保持不变,并且永远不会重新计算。而`memoizedFunction`则返回一个函数,这个函数只有在`values`数组发生变化时才会重新计算。

### 渲染和React

在React中,记忆化非常重要,因为它与React的渲染机制密切相关。React使用存储在内存中的虚拟DOM来比较数据并决定更新什么。虚拟DOM帮助React提高性能并让应用程序保持快速。默认情况下,如果您的组件中的任何值发生更改,整个组件将重新渲染。这使得React对用户输入具有“反应性”,并允许屏幕更新而无需重新加载页面。

React useCallback钩子使用指南与性能优化技巧

然而,您可能不希望某些组件在值未发生变化时重新渲染。这就是通过useCallback和useMemo进行记忆化的地方。当React重新渲染您的组件时,它也会重新创建您在组件中声明的函数。由于函数被视为新对象,即使它们具有相同的代码,它们也不会被认为是相同的。

### React useCallback的性能优势

如果useCallback使用得当,它可以帮助加速您的应用程序并防止组件在不需要时重新渲染。例如,假设您有一个组件,它获取大量数据并负责以图表或图形的形式显示该数据。如果父组件重新渲染,但更改的道具或状态不会影响该组件,您可能不想或不需要重新渲染它并重新获取所有数据。避免这种重新渲染和重新获取可以节省用户的带宽并提供更流畅的用户体验。

### React useCallback的缺点

尽管useCallback可以帮助提高性能,但它也有其缺点。在使用useCallback(和useMemo)之前,需要考虑以下几点:

1. **垃圾收集**:React将丢弃其他尚未记忆的函数以释放内存。
2. **内存分配**:与垃圾回收类似,您拥有的记忆功能越多,需要的内存就越多。每次您使用这些回调时,React中都有一堆代码需要使用更多的内存来为您提供缓存的输出。
3. **代码复杂性**:当您开始在这些钩子中包装函数时,您会立即增加代码的复杂性。现在需要更多地了解为什么使用这些钩子并确认它们被正确使用。

意识到以上这些陷阱可以让你省去自己跌跌撞撞的头痛。在考虑使用useCallback时,请确保性能优势大于缺点。

### React使用回调示例

下面是一个带有Button组件和Counter组件的简单设置。Counter有两个状态并渲染出两个Button组件,每个组件将更新Counter组件状态的一个单独部分。

“`javascript
import { useCallback, useState } from ‘react’;
const Button = ({handleClick, name}) => {
console.log(`${name} rendered`);
return {name};
};
const Counter = () => {
console.log(‘counter rendered’);
const [countOne, setCountOne] = useState(0);
const [countTwo, setCountTwo] = useState(0);
return (

React useCallback钩子使用指南与性能优化技巧

{countOne}

);
};
“`

在此示例中,无论何时单击任一按钮,您都会在控制台中看到:

“`
counter rendered
button1 rendered
button2 rendered
“`

现在,如果我们应用useCallback到我们的handleClick函数并将我们的Button组件包装在React.memo中,我们可以看到useCallback为我们提供了什么。React.memo类似于useMemo并且允许我们记忆一个组件。

“`javascript
import { useCallback, useState } from ‘react’;
const Button = React.memo(({handleClick, name}) => {
console.log(`${name} rendered`);
return {name};
});
const Counter = () => {
console.log(‘counter rendered’);
const [countOne, setCountOne] = useState(0);
const [countTwo, setCountTwo] = useState(0);
const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne]);
const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]);
return (

{countOne}

);
};
“`

现在,当我们单击任一按钮时,我们只会看到我们单击以登录控制台的按钮:

“`
counter rendered
button1 rendered
counter rendered
button2 rendered
“`

我们已经将memoization应用于我们的按钮组件,并且传递给它的prop值被视为相等。这两个handleClick函数被缓存并且将被React视为同一个函数,直到依赖数组中的项目的值发生变化(例如countOne, countTwo)。

### 小结

尽管useCallback和useMemo很酷,但请记住它们有特定的用例——您不应该用这些钩子包装每个函数。如果函数在计算上很复杂,那么传递给记忆组件的另一个钩子或道具的依赖关系是很好的指标,您可能希望获得useCallback。我们希望这篇文章能帮助你理解这个高级的React功能,并帮助你在使用函数式编程的过程中获得更多的信心!

文章网址:https://www.wpbull.com/jiqiao/1652.html