useMemo缓存结果,useCallback缓存函数
1、看一下useMemo和useCallback底层源码的区别
useMemo 源码实现
组件首次渲染时 useMemo 的源码实现:
// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function mountMemo<T>(
nextCreate: () => T, // “创建”函数
deps: Array<mixed> | void | null, // 依赖项
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate(); // 执行 "创建"函数
hook.memoizedState = [nextValue, nextDeps]; // 将 "创建"函数 执行后的返回值缓存起来
return nextValue; // 返回缓存后的变量值
}
useMemo 的依赖项发生变化时useMemo的源码实现:
// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
依赖对比:
function areHookInputsEqual(nextDeps, prevDeps) {
if (prevDeps === null) {
{
warning$1(false, '%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
}
return false;
}
{
// Don't bother comparing lengths in prod because these arrays should be
// passed inline.
if (nextDeps.length !== prevDeps.length) {
warning$1(false, 'The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, '[' + nextDeps.join(', ') + ']', '[' + prevDeps.join(', ') + ']');
}
}
for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
;
}
注意:上面react包括state和props对比都是采用shallowEaque,只比较一层。且依赖是对象时X===Y不同对象即使内容一样仍然return false,如下案例,除非前两种useMemo才不会更新,否则每次都更新,useEffect及useCallback同理,useMemo常用来缓存组件,所以不要乱用useMemo等hooks
const [obj, setObj] = useState({a:1})
//如点击等导致state更新
setObj(obj)
setObj(preObj=>preObj)
setObj(preObj=>{...preObj})
useMemo(()=>{....},[obj]
useCallback 源码实现
组件首次渲染时 useCallback 的源码实现:
// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
useCallback 的依赖项发生变化时useMemo的源码实现:
// React 版本:16.13.1
// react-reconciler/src/ReactFiberHooks.new.js
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}
useMemo 和 useCallback 都是返回一个缓存后的值,useMemo 返回的是一个缓存后的变量,而useCallback 返回的是一个缓存后的回调函数。可以在 useMemo 的 "创建函数" 中返回一个回调函数来模拟 useCallback 的功能。useMemo 和 useCallback 可以用来解决函数组件更新过程中的性能问题。
// 使用 useCallback 缓存函数
const addClick = useCallback(() => {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
}, [count]);
// 使用 useMemo替代useCallback 缓存函数
const addClick = useMemo(() => () => {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
}, [count]);