ReactJS - useCallback
useCallback Hooks(钩子)类似于 useMemo Hooks(钩子),提供记忆函数而不是值的功能。由于回调函数是 JavaScript 编程不可或缺的一部分,并且回调函数是通过引用传递的,因此 React 提供了一个单独的Hooks(钩子) useCallback 来记忆回调函数。理论上,useCallback 功能可以使用 useMemo Hooks(钩子)本身来实现。但是,useCallback 提高了 React 代码的可读性。
useCallback Hooks(钩子)的签名
useCallback Hooks(钩子)的签名如下 −
const <memoized_callback_fn> = useCallback(<callback_fn>, <dependency_array>);
此处,useCallback 接受两个输入并返回一个记忆化回调函数。输入参数如下 −
callback_fn − 要记忆的回调函数。
dependency_array − 保存回调函数所依赖的变量。
useCallback Hooks(钩子)的输出是 callback_fn 的记忆化回调函数。useCallback Hooks(钩子)的用法如下 −
const memoizedCallbackFn = useCallback(() => { // code }, [a, b])
注意 − useCallback(callback_fn, dependency_array) 相当于 useMemo(() => callback_fn, dependency_array)。
应用 useCallback
让我们通过创建一个 React 应用程序来学习如何应用 useCallback Hooks(钩子)。
首先,使用 create-react-app 命令创建并启动一个 React 应用程序,如下所示 −
create-react-app myapp cd myapp npm start
接下来,在 components 文件夹下创建一个组件 PureListComponent (src/components/PureListComponent.js)
import React from "react"; function PureListComponent() { return <div>List</div> } export default PureListComponent
接下来,使用 PureListComponent 组件更新根组件,如下所示−
import PureListComponent from './components/PureListComponent'; function App() { return ( <div style={{ padding: "5px" }}> <PureListComponent /> </div> ); } export default App;
接下来,打开 PureListComponent.js 并添加两个 props
组件中要显示的项目列表
回调函数,用于在控制台中显示用户单击的项目
import React from "react"; function PureListComponent(props) { const items = props['items']; const handleClick = props['handleClick'] console.log("I am inside the PureListComponent") return ( <div> {items.map((item, idx) => <span key={idx} onClick={handleClick}>{item} </span> )} </div> ) } export default React.memo(PureListComponent)
这里我们有,
使用 items props 获取项目列表
使用 handleClick 获取点击事件的处理程序
使用 React.memo 包装组件以记忆组件。由于组件将为给定的一组输入呈现相同的输出,因此它在 react 中被称为 PureComponent。
接下来,让我们更新 App.js 并使用 PureListComponent,如下所示 −
import React, {useState, useEffect} from 'react'; import PureListComponent from './components/PureListComponent'; function App() { // 数字数组 var listOfNumbers = [...Array(100).keys()]; // 回调函数 const handleCallbackFn = (e) => { console.log(e.target.innerText) } const [currentTime, setCurrentTime] = useState(new Date()) useEffect(() => { let interval = setInterval(() => { setCurrentTime(new Date()) }, 1000) return () => clearInterval(interval) }, [currentTime]) return ( <div style={ { padding: "5px" } }> <PureListComponent items={listOfNumbers} handleClick={handleCallbackFn}/> <div>Time: <b>{currentTime.toLocaleString().split(',')[1]}</b></div> </div> ); } export default App;
这里我们包含了一个状态 currentTime,并使用 setInterval 每秒更新一次,以确保组件每秒重新渲染一次。
我们可能认为 PureListComponent 不会每秒重新渲染一次,因为它使用了 React.memo。但是,它会重新渲染,因为 props 值是引用类型。
接下来,更新根组件并使用 useMemo 和 useCallback 来保存数组和回调函数,如下所示 −
import React, {useState, useEffect, useCallback, useMemo} from 'react'; import PureListComponent from './components/PureListComponent'; function App() { // 数字数组 const listOfNumbers = useMemo(() => [...Array(100).keys()], []); // 回调函数 const handleCallbackFn = useCallback((e) => console.log(e.target.innerText), []) const [currentTime, setCurrentTime] = useState(new Date()) useEffect(() => { let interval = setInterval(() => { setCurrentTime(new Date()) }, 1000) return () => clearInterval(interval) }, [currentTime]) return ( <div style={ { padding: "5px" } }> <PureListComponent items={listOfNumbers} handleClick={handleCallbackFn}/> <div>Time: <b>{currentTime.toLocaleString().split(',')[1]}</b></div> </div> ); } export default App;
这里我们有,
使用 useMemo 保存 items 数组
使用 useCallback 保存 handleClick 回调函数
最后,在浏览器中检查应用程序,它不会每秒重新渲染 PureListComponent。

useCallback 的用例
useCallback Hooks(钩子)的一些用例如下 −
带有函数的纯函数组件props
useEffect 或其他具有函数依赖项的Hooks(钩子)
在防抖动/节流或类似操作期间使用函数
优点
useCallback Hooks(钩子)的优点如下 −
易于使用的 API
易于理解
提高应用程序的性能
缺点
从技术上讲,useCallback 没有缺点。但是,在应用程序中大量使用 useCallback 会带来以下缺点。
降低应用程序的可读性
降低应用程序的可理解性
应用程序调试很复杂
开发人员应该对 JavaScript 语言有深入的了解才能使用它
总结
useCallback 易于使用并可提高性能。同时,useCallback 应仅在绝对必要时使用。它不应该在每个场景中都使用。一般规则是检查应用程序的性能。如果需要改进,那么我们可以检查 useCallback 的使用是否有助于提高性能。如果我们得到积极的回应,那么我们可以使用它。否则,我们可以把改进和优化应用程序性能的工作交给 React。