我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

useCallback缓存的是函数,意思是:无论组件如何刷新重新渲染,定义在组件内部的被useCallback的函数(的内存指向)都不变。和专门缓存组件的高阶函数memo()是完美搭配的关系。memo()负责缓存组件,组件传递的props里面的函数定义,由useCallback进行处理,以保持引用不变。当然,一些钩子的导出函数(useState()的导出等)本来就是不变的,并不需要useCallback进行处理。

苏南大叔:React缓存,useCallback钩子如何保持函数不变?配合memo - usecallback-保持函数不变
React缓存,useCallback钩子如何保持函数不变?配合memo(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0create-react-app@5.0.1react-router-dom@6.27.0react@18.3.1useCallback()是个钩子,所以只能用在函数式组件里面。本文的第一个代码可以表明:useCallback不仅可以作用于组件内部的自定义函数上,还可以作用于普通变量,数组甚至对象上。

判断函数/变量是否改变

毕竟内存引用地址这个东西,是看不见摸不着的。用来解释原理,可以。但是,总是有些牵强。这里使用一段代码来更加直观的解释这个问题。

import React, { useCallback, useState } from "react";
const set_var = new Set();
const set_var2 = new Set();
const set_arr = new Set();
const set_arr2 = new Set();
const set_obj = new Set();
const set_obj2 = new Set();
const set_fn1 = new Set();
const set_fn2 = new Set();
export default function App() {
  const [a, setA] = useState(1);
  let var1 = "变量";
  let var2 = useCallback(var1, []);
  set_var.add(var1);
  set_var2.add(var2);
  let arr1 = [1];
  let arr2 = useCallback(arr1, []);
  set_arr.add(arr1);
  set_arr2.add(arr2);
  let obj1 = {"s":"n"};
  let obj2 = useCallback(obj1, []);
  set_obj.add(obj1);
  set_obj2.add(obj2);
  let fn1 = () => {};
  let fn2 = useCallback(fn1, []);
  set_fn1.add(fn1);
  set_fn2.add(fn2);
  return (
    <div>
      <button onClick={() => setA(a + 1)}>修改a:{a},引发父级渲染</button>
      <br />
      变量:{set_var.size},{set_var2.size},{Array.from(set_var2).join(',')}
      <br />
      数组:{set_arr.size},{set_arr2.size},{Array.from(set_arr2).join(',')}
      <br />
      对象:{set_obj.size},{set_obj2.size},{Array.from(set_obj2).map((x)=>JSON.stringify(x))}
      <br />
      函数:{set_fn1.size},{set_fn2.size}
      <br />
    </div>
  );
}

这里的原理就是:set()天然去重,如果每次组件渲染时的变量或者函数,被添加到set()里面后,被接受了,那么就证明已经不是同一个变量/函数了。

苏南大叔:React缓存,useCallback钩子如何保持函数不变?配合memo - set检验真伪
React缓存,useCallback钩子如何保持函数不变?配合memo(图3-2)

这个例子,还说明了一个问题。如果一个函数或者变量与状态无关,那么,它就没有什么必要非要定义在组件函数体里面。提升一级,反而是更好的选择,节约下内存。

useCallback 测试代码

其实,useCallback()钩子的原理,已经在本文开头说的很明白了。记住:useCallback()保持的是函数(的内存地址)不变,函数体本身没有被执行。useMemo()保持的是函数的运算结果不变,函数体被执行了。

import React, { useCallback, useState, useMemo } from "react";
function Child({ alt }) {
  // console.log('Child')
  return (
    <div>
      {alt}, {Date.now().toString().slice(-6)}
    </div>
  );
}
const MemoChild = React.memo(Child);

export default function App() {
  console.log("app");
  const [a, setA] = useState(1);
  let fn = () => {
    console.log("run", a);
    return a + 1;
  };
  let fn2 = useCallback(fn, []); // 这个返回的是函数,没被执行
  let result = useMemo(fn, []); // 这个是返回的是结果,被执行了
  let result2 = useMemo(fn, [a]); // 这个是返回的是结果,被执行了,a变它就变
  return (
    <div>
      <button onClick={() => setA(a + 1)}>修改a:{a},引发父级渲染</button>
      <h4>useMemo(依赖项):</h4>
      恒定不变{result}, a变它就变{result2}
      <br />
      <h4>memo+useCallback(函数props):</h4>
      <Child alt="恒定变化" />
      <MemoChild fn={fn} alt="恒定变化" />
      <MemoChild fn={setA} alt="恒定不变" />
      <MemoChild fn={fn2} alt="恒定不变" />
    </div>
  );
}

代码很简单,不解释。只要就是对比阐述useMemo()useCallback()的区别。

苏南大叔:React缓存,useCallback钩子如何保持函数不变?配合memo - usememo和usecallback
React缓存,useCallback钩子如何保持函数不变?配合memo(图3-3)

相关文章

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react