
useCallback 的基本使用方法useCallback 是 React 的一个核心 Hook,用于缓存函数定义,避免组件重新渲染时重复创建函数实例。以下是其基本使用方法:
const memoizedCallback = useCallback(
() => {
// 函数逻辑 (例如更新状态、调用API等)
doSomething(a, b);
},
[a, b] // 依赖项数组
);useCallback 后可复用函数。React.memo)时,可避免子组件不必要的重渲染。import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// 缓存函数:依赖项为空数组,函数只创建一次
const increment = useCallback(() => {
setCount(prev => prev + 1); // 使用函数式更新避免闭包问题
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}[] 表示函数仅在组件初次渲染时创建。setCount(prev => prev + 1) 替代 setCount(count + 1) 可避免闭包陷阱(函数捕获过时状态)。useCallback,本质上是用于缓存函数。
如果函数,是以props的方式,传递给子组件,为了每次避免子组件的渲染,建议使用useCallback进行包裹。
但是每一次,使用useCallback,我们考虑的首要问题是,这样真的优化了组件的性能吗?其实大多数场景,如果不是类似列表渲染的场景,这样不一定会优化了性能。
也就是,函数作为props传递给性能敏感的子组件的场景,才是使用useCallback的时候。
useCallback 的原理解析useCallback 的主要目的是在依赖项不变的情况下,返回同一个函数引用,避免函数重复创建,从而优化性能。useCallback它会在首次渲染时(或依赖项变化时)创建一个新的函数,并将其缓存起来。在后续渲染中,如果依赖项没有变化,则返回缓存的函数;否则,就重新创建函数并更新缓存。let lastDeps; // 上一次的依赖项
let lastCallback; // 上一次缓存的函数
function useCallback(callback, deps) {
if (lastDeps === undefined) {
// 第一次调用
lastDeps = deps;
lastCallback = callback;
return callback;
}
// 检查依赖项是否变化
const hasChanged = deps.some((dep, index) => dep !== lastDeps[index]);
if (hasChanged) {
lastDeps = deps;
lastCallback = callback;
}
return lastCallback;
}每次掉用useCallback,返回的函数,取决于依赖项有没有发生变化。
React内部是咋样的呢?
React 在 Fiber 节点(组件实例对应的数据结构)中维护一个 memorizedState 链表,专门存储 Hooks 状态。
function updateCallback(callback, deps) {
const hook = updateWorkInProgressHook(); // 获取当前 Hook 节点
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState; // 读取缓存的上次状态
// 依赖项对比:使用浅比较(shallow equal)
if (prevState !== null && areHookInputsEqual(nextDeps, prevState[1])) {
return prevState[0]; // 返回缓存的函数
}
// 依赖变化:缓存新函数
hook.memoizedState = [callback, nextDeps];
return callback;
}源码中的 areHookInputsEqual 对依赖数组进行浅比较(类似 Object.is):
function areHookInputsEqual(nextDeps, prevDeps) {
if (prevDeps === null) return false;
for (let i = 0; i < prevDeps.length; i++) {
if (!Object.is(nextDeps[i], prevDeps[i])) {
return false;
}
}
return true;
}这种优化避免了深度比较的性能损耗
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。