React教程,如何使用useCallback函数来缓存函数逻辑变量?
发布于 作者:苏南大叔 来源:程序如此灵动~
继续聊react项目中函数式组件的useXxx系列,本文聊另外一个缓存函数而不是缓存函数结果的useCallback。和useMemo对比的话,useMemo是缓存的函数运算结果,而useCallback是缓存的函数逻辑中涉及的变量的值+逻辑。监控条件符合【变量变化】的时候,就会从新获取函数中变量取值。

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

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,没有监控点

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,并添加监控开关

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值。

案例四,使用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的缓存意义在哪里呢?不解释。

案例五【推荐】
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就不会更新,子组件就不会更新。达到节约性能的目的。

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

相关文章:
相关链接
- https://newsn.net/say/react-useref.html
- https://newsn.net/say/react-usestate.html
- https://newsn.net/say/react-usememo.html
- https://newsn.net/say/react-useeffect.html
结束语
更多react相关经验文章,请点击苏南大叔的博客文章: