前言
useMome和useCallback实现原理完全一致
useMemo(() => fn, []); //等效于 useCallback(fn, []);
不同的点
useCallback
//第一个参数接收一个函数,useCallback调用后返回一个新的被缓存的记忆函数 useCallback(fn, []); //return 一个新的function
useMemo
//第一个参数接收一个函数,useMemo调用后返回第一个参数函数return的被缓存的记忆的值 useMemo(() => fn, []); //return 一个fn的记忆function useMemo(() => obj, []); //return 一个obj的记忆Object
作用影响
测试代码
function Home() { const [state, setState] = useState("initialization"); //普通函数 const fn = () => { console.log("普通函数输出:", state); }; //记忆函数,这里第二个参数设置为[],表示不依赖任何值,只在组件初始化时创建memoizedFn,组件更新时不更新memoizedFn const memoizedFn = useCallback(() => { console.log("memoized函数输出:", state); }, []); //组件Home,mount 和 update时都执行 fn(); memoizedFn(); const update = () => { setState("initialization" + new Date().getTime()); }; return ( <div> <div>state值:{state}</div> <button onClick={update}>改变state</button> </div> ); }
作用
1.在组件初始化时,fn 和 memoizedFn 都会拿取到state的最新值initialization吗?
2.在组件更新后只要依赖的项没有发生变化,那么memoizedFn输出的结果永远是旧值?
3.如果使用的是非响应式(useState())的普通变量,memoizedFn还会保留它吗?
4.多个memoizedFn嵌套使用,该如何缓存结果?
验证
1.【验证1】在组件初始化时,fn 和 memoizedFn 都会拿取到state的最新值initialization
可以看到在初始化时,可以看到普通函数 fn 和 记忆函数memoizedFn 都打印出了state的初始值:inittialization
2.【验证2】在组件更新后只要依赖的项没有发生变化,那么memoizedFn输出的结果永远是旧值
可以看到当组件更新后,普通函数fn 读取到了最新的state值:initialization1640850521426,而memoizedFn函数,输出的还是组件创建时的旧state值:inittialization,这就意味着当依赖项(我们这里设置的:[]),没有改变时无论我们执行多少次memoizedFn函数,始终输出的都是上一次更新或者时创建时的旧值,在memoizedFn使用的所有变量(state),都是被缓存的旧值
3.【验证3】.如果使用的是非响应式(useState())的普通变量,memoizedFn还会保留它吗
修改代码
function Home() { const [state, setState] = useState("initialization"); let normal = "init noraml"; //增加normal变量 //普通函数 const fn = () => { console.log("普通函数输出:", state, "noraml:", normal); //增加输出 }; //记忆函数 const memoizedFn = useCallback(() => { console.log("memoized函数输出:", state, "noraml:", normal); //增加输出 }, []); //组件Home,mount 和 update时都执行 //... const update = () => { const date = new Date().getTime(); setState("initialization" + date); normal = "init noraml" + date; //增加输出 }; //... }
可以看到,在第一次更新state时,memoizedFn 并没有缓存noraml的旧值init noraml,而在第二次更新时,使用的是第一次更新时的缓存值,这是一个很奇怪的点,官方上并没有给出答案,但是却推荐,不要在useCallback中使用外部的普通变量,尽量在useCallback类定义变量,以确保变量能得到我们预期的结果
const memoizedFn = useCallback(() => { let normal = "init noraml"; console.log("memoized函数输出:", state, "noraml:", normal); //增加输出 }, []);
4.【验证4】多个memoizedFn嵌套使用,该如何缓存结果
修改代码
function Home() { const [state, setState] = useState("initialization"); //记忆函数1 无任何依赖 const memoizedFn1 = useCallback(() => { console.log("memoized函数1 输出:", state); console.log("memoized函数1调用memoizedFn2"); memoizedFn2(); }, []); //记忆函数1 无任何依赖 const memoizedFn2 = useCallback(() => { console.log("memoized函数2 输出:", state); }, [state]); //组件Home,mount 和 update时都执行 memoizedFn1();
console.log("直接调用memoizedFn2"); memoizedFn2();
const update = () => { const date = new Date().getTime(); setState("initialization" + date); //使用setState改动state console.log("=====组件更新====="); }; return ( <div> <div>state值:{state}</div> <button onClick={update}>改变state</button> </div> ); }
可以看出,在memoizedFn1中的执行的memoizedFn2,即便memoizedFn2中设置的依赖[state]发生更新,memoizedFn2读取的state仍是旧值,这就意味着在memoizedFn内部的函数,只要最外层的memoized不发生更新,那么内部函数使用的所有变量都为旧值