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

继续聊react项目中函数式组件的useXxx系列,本文聊另外一个缓存函数而不是缓存函数结果的useCallback。和useMemo对比的话,useMemo是缓存的函数运算结果,而useCallback是缓存的函数逻辑中涉及的变量的值+逻辑。监控条件符合(变量变化)的时候,就会从新获取函数中变量取值。

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - react-usecallback-hero
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-1)

大家好,这里是苏南大叔的程序如此灵动博客,这里记录苏南大叔和计算机代码的故事。本文描述useCallback的使用方法。本文的主要需求是对list列表进行维护,通过useCallback的不同包装,具有不同的运行结果。测试环境:create-react-app@5.0.1react@18.2.0react-dom@18.2.0node@16.14.2

案例一,不使用useCallback

不使用useCallback,先使用普通的函数addItem

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-code-1
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-2)

import React, { useState, useEffect, useCallback } from 'react';

function List() {
  let [list, setList] = useState([]);
  const addItem = () => {
    console.log(list);
    list.push('sunan大叔 ' + Date.now().toString().slice(-6));
    setList([...list]);
  };
  useEffect(() => {
    setList(['苏南大叔', 'sunan大叔']);
  }, []);
  return (
    <>
      {
        list.map((item, index) => {
          return (
            <div key={index}>
              {item}
            </div>
          )
        })
      }
      <button onClick={addItem.bind()}>不使用useCallback</button>
    </>
  )
}
export default List;

案例二,使用useCallback,没有监控点

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-code-2
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-3)

import React, { useState, memo, useEffect, useCallback } from 'react';

function List() {
  let [list2, setList2] = useState([]);
  const addItem2 = useCallback(() => {
    console.log(list2);    // []
    list2.push('苏南大叔 ' + Date.now().toString().slice(-6));
    setList2([...list2]);
  }, []);
  useEffect(() => {
    setList2(['苏南大叔', 'sunan大叔']);
  }, []);
  return (
    <>
      {
        list2.map((item, index) => {
          return (
            <div key={index}>
              {item}
            </div>
          )
        })
      }
      <button onClick={addItem2.bind()}>useCallback被缓存但没监控</button>
    </>
  )
}
export default List

在这个例子中,虽然使用了useCallback(),但是没有添加监控点。所以,useCallback()的函数,虽然被缓存了,但是内部的变量并没有取到正确的值,list取到的值是state初始值[],在useEffect的初始化动作,显然发生在了useCallback()之后。所以,在界面上点击按钮的时候,原始的值会被覆盖。

案例三,使用useCallback,并添加监控开关

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-code-3
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-4)

import React, { useState, memo, useEffect, useCallback } from 'react';

function List() {
  let [list3, setList3] = useState([]);
  let [list3Plus, setlist3Plus] = useState(false);
  const addItem3 = useCallback(() => {
    console.log(list3); // 符合监控条件的时候,会刷新这里面的所有变量取值
    list3.push('苏南大叔 ' + Date.now().toString().slice(-6));
    setList3([...list3]);
  }, [list3Plus]);
  const addItem3Plus = function () {
    setlist3Plus(!list3Plus);
  }
  useEffect(() => {
    setList3(['苏南大叔', 'sunan大叔']);
  }, []);
  return (
    <>
      {
        list3.map((item, index) => {
          return (
            <div key={index}>
              {item}
            </div>
          )
        })
      }
      <button onClick={addItem3.bind()}>useCallback默认没开启监控</button>
      <button onClick={addItem3Plus.bind()}>开启监控{list3Plus.toString()}</button>
    </>
  )
}
export default List

这个例子中,使用了useCallback(),并且添加了监控值list3Plus。默认情况下,因为监控值list3Plus没有发生变化,所以,函数内部拿到的list状态值,仍然是[](参考上一条)。通过第二个附加按钮修改了list3Plus之后,useCallback()被刷新,得到了最新的list值,然后再继续添加维护这个list值。

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-code-4
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-5)

安装四,使用useCallback,并添加合适的监控点

import React, { useState, memo, useEffect, useCallback } from 'react';

function List() {
  let [list3, setList3] = useState([]);
  const addItem3 = useCallback(() => {
    console.log(list3); // 符合监控条件的时候,会刷新这里面的所有变量取值
    list3.push('苏南大叔 ' + Date.now().toString().slice(-6));
    setList3([...list3]);
  }, [list3]);
  useEffect(() => {
    setList3(['苏南大叔', 'sunan大叔']);
  }, []);
  return (
    <>
      {
        list3.map((item, index) => {
          return (
            <div key={index}>
              {item}
            </div>
          )
        })
      }
      <button onClick={addItem3.bind()}>useCallback开启合适监控点</button>
    </>
  )
}
export default List

这里,添加了更加合适的监控点list3,不过,这样的话,实际上这个useCallback的缓存意义在哪里呢?不解释。

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-code-5
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-6)

案例四【推荐】

import React, { useState, useCallback } from 'react';
const SunanMemo = () => {
  const [count, setCount] = useState(0)
  const [flag, setFlag] = useState(true)
  const increase = useCallback(() => {
    setCount(count + 1)
  }, [count])
  return (
    <div>
      <div>
        <div>【count: {count}】count不变,useCallback就不变</div><br />
        <TestButton title="普通点击" onClick={() => setCount(count + 1)} />
        <TestButton title="Callback+bind" onClick={increase.bind()} />
        <TestButton title="Callback" onClick={increase} />
      </div>
      <button onClick={() => setFlag(!flag)}>更新非count的状态,useCallback组件不更新</button>
    </div>
  )
}
const TestButton = React.memo((props) => {
  console.log("props变化,组件就会更新:" + props.title)
  return <button onClick={props.onClick}>{props.title}</button>
})
export default SunanMemo;

useCallback实际上真正的用法是与memo联合使用的,用于缓存传入memo组件中的函数(作为props传入)。memo过的组件当props发生变化的时候,才会更新。而把函数作为props传入的时候,总是会引起子组件的更新。这个时候,就需要useCallback出场了。函数只有依赖项更新的时候,才会变化。不变化的话,子组件的props就不会更新,子组件就不会更新。达到节约性能的目的。

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - usecallback-memo
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-7)

然而,这里的useCallback的函数,使用.bind()的话,就会破坏这个useCallback效果。比如上文中的第二个子组件按钮和第三个。一个.bind()就破坏了全局。

苏南大叔:react教程,如何使用useCallback函数来缓存函数逻辑变量? - 运行截图
react教程,如何使用useCallback函数来缓存函数逻辑变量?(图8-8)

相关文章:

相关链接

结束语

更多react相关经验文章,请点击苏南大叔的博客文章:

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

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

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

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