useMemoizedFn文档地址:https://ahooks.js.org/zh-CN/hooks/use-memoized-fn
hooks组件内什么时候会更新自定义函数
在 React 中,自定义的 Hooks 内部的函数在以下常见的几种情况下会被重新赋值,导致更新引用:
-
组件重新渲染:
当组件重新渲染时,Hooks 内部的函数会被重新执行,从而导致函数的重新赋值和更新引用。
这意味着每次组件重新渲染时,Hooks 内部的函数会被重新计算,返回新的函数引用。 -
依赖项发生变化(对于使用了依赖项的 Hooks):
对于某些 Hooks,例如 useEffect、useMemo 和 useCallback,当其依赖项发生变化时,Hooks 内部的函数也会被重新执行。
这会导致函数的重新赋值和更新引用,以确保在依赖项发生变化时能够获得最新的函数引用。 -
闭包内部依赖的状态发生变化:
自定义的 Hooks 可能使用闭包内部的状态或其他上下文中的值作为依赖,如果这些依赖的状态发生变化,Hooks 内部的函数也会重新执行。这样做是为了保持函数内部依赖的状态是最新的,避免出现闭包中使用过期数据的问题。
useCallback和useMemoizedFn对比
在 React 中,useMemoizedFn
和 useCallback
都用于优化函数组件的性能,但它们有一些不同点。
理论上,可以使用 useMemoizedFn 完全代替 useCallback。
import React, { useState, useCallback } from 'react';
import { message } from 'antd';
import { useMemoizedFn } from 'ahooks';
export default () => {
const [count, setCount] = useState(0);
const callbackFn = useCallback(() => {
message.info(`Current count is ${count}`);
}, [count]);
const memoizedFn = useMemoizedFn(() => {
message.info(`Current count is ${count}`);
});
return (
<>
<p>count: {count}</p>
<button
type="button"
onClick={() => {
setCount((c) => c + 1);
}}
>
Add Count
</button>
<div style={{ marginTop: 16 }}>
<button type="button" onClick={callbackFn}>
call callbackFn
</button>
<button type="button" onClick={memoizedFn} style={{ marginLeft: 8 }}>
call memoizedFn
</button>
</div>
</>
);
};
在上面的示例中,callbackFn和memoizedFn效果是一样的,区别在于memoizedFn不用自己指定依赖。
useCallback:使用useMemo实现
使用 useMemo
来实现类似于 useCallback
的功能。事实上,useCallback
本质上就是使用 useMemo
来进行函数记忆化的一种简化形式。
下面是一个使用 useMemo
实现类似于 useCallback
的示例:
import React, { useState, useMemo } from 'react';
function useMyCallback(callback, deps) {
return useMemo(() => callback, deps);
}
const MyComponent = () => {
const [count, setCount] = useState(0);
// 使用 useMyCallback 记忆化函数
const handleClick = useMyCallback(() => {
console.log('Callback function called');
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
在上面的示例中,我们定义了一个 useMyCallback
Hook,它接受一个函数 callback
和一个依赖项列表 deps
。在内部,我们使用 useMemo
将传入的 callback
进行记忆化,并返回记忆化的函数引用。
这样,当 count
不变时,handleClick
将返回相同的函数引用,避免了因为父组件重新渲染而导致的函数的频繁创建,从而实现了和 useCallback
类似的功能。
useMemoizedFn:使用useMemo实现
useMemoizedFn
是持久化 function 的 Hook,理论上,可以使用 useMemoizedFn 完全代替 useCallback。使用 useMemoizedFn,可以省略第二个参数 deps,同时保证函数地址永远不会变化。
function useMemoizedFn(func){
if(typeof func !== 'function') return
// 通过 useRef 保持其引用地址不变,并且值能够保持值最新
const funcRef = useRef(func)
funcRef.current = useMemo(()=>{
return func
}, [func])
const memoizedFn = useRef();
if (!memoizedFn.current) {
// 返回的持久化函数,调用该函数的时候,调用原始的函数
memoizedFn.current = function(...args){
return funcRef.current.apply(this, args)
}
}
return memoizedFn.current
}
ahooks源码实现如下:
import { useMemo, useRef } from 'react';
import { isFunction } from '../utils';
import isDev from '../utils/isDev';
function useMemoizedFn(fn) {
if (isDev) {
if (!isFunction(fn)) {
console.error("useMemoizedFn expected parameter is a function, got ".concat(typeof fn));
}
}
var fnRef = useRef(fn);
// why not write `fnRef.current = fn`?
// https://github.com/alibaba/hooks/issues/728
fnRef.current = useMemo(function () {
return fn;
}, [fn]);
var memoizedFn = useRef();
if (!memoizedFn.current) {
memoizedFn.current = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return fnRef.current.apply(this, args);
};
}
return memoizedFn.current;
}
export default useMemoizedFn;
useMemoizedFn
的实现中使用了两个 useRef
钩子,具有不同的用途:
-
fnRef
:这个useRef
用于存储原始函数(fn
)的引用。它被初始化为传递给useMemoizedFn
函数的fn
参数,并且其目的是始终保持函数的最新版本。使用它的原因是确保记忆化的函数(memoizedFn
)能够始终调用最新版本的原始函数fn
,即使在渲染之间fn
发生了变化。 -
memoizedFn
:这个useRef
用于存储将由useMemoizedFn
钩子返回的记忆化函数。其目的是在渲染间保持记忆化函数的引用。当memoizedFn
首次创建时,它被赋值为一个新的函数,该函数调用最新版本的fnRef.current
并传递提供的参数。然后,这个记忆化函数被返回并在组件中使用。
这样的实现通过使用两个独立的 useRef
钩子,确保记忆化函数能够正确地引用最新版本的原始函数,同时保持记忆化函数本身的稳定引用。
使用 useMemo
更新 fnRef.current
而不是直接赋值 fnRef.current = fn
,是为了确保当 fn
依赖项发生变化时,记忆化函数会被重新计算。通过在 useMemo
中使用 [fn]
作为依赖项数组,记忆化函数将始终引用 fn
的最新版本,并在 fn
发生变化时相应地更新。
这个实现旨在确保 useMemoizedFn
返回的记忆化函数保持高效,并正确地反映原始函数的最新版本和其依赖项的最新变化。
参考文章
https://juejin.cn/post/7106061970184339464#heading-6文章来源:https://www.toymoban.com/news/detail-620146.html
https://ahooks.js.org/zh-CN/hooks/use-memoized-fn文章来源地址https://www.toymoban.com/news/detail-620146.html
到了这里,关于[React]useMemoizedFn和useCallback对比的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!