业务背景
我们有一个需求是在用户输入用例名称的时候,系统根据名称去匹配公共用例库中的用例模块展示在下拉框中,然后用户可选择想要的模块导入其中的用例:
但是如果用户每输入一个字符就去调用接口查询的话,这样就太频繁了,会给服务端造成不小的压力,所以就需要使用防抖方法来进行限流控制。这里的方案如下:
因为antd提供了AutoComplete
方法能够很方便的在表格中实现编辑行的操作,这里也就不重新造轮子了,然后结合lodash
提供的debounce
方法进行防抖设置,也就是每隔200毫秒去搜索一次,完整的代码如下:
const batchSearchDebounce = debounce(async (keyWords) => {
if (keyWords) {
return await searchComStdMod({ search: keyWords }).then((res) => {
const data: any[] = [];
res.data.forEach((item: any) => {
data.push({
label: (
<>
<Tag color="blue">公共模块</Tag>
{item.label}
</>
),
value: item.value,
name: item.label,
});
});
setSearchData(data);
});
}
}, 200);
<AutoComplete
style={{ width: width }}
placeholder="请输入用例名称"
onSelect={(_: any, mod: any) => {
comStandardCasesAll({ module_id: mod.value }, REQ_LIST).then((res) => {
setFormData({ cases: res.data, module_name: mod.name });
setVisible(true);
});
}}
{...(type === 'single' ? {} : { value: batchData.value })}
onChange={async (keyWords) => {
batchData.onChange(keyWords);
await batchSearchDebounce(keyWords); //防抖会失效
}}
options={searchData}
/>
但在使用的过程中发现防抖并没有生效,我每输入一个文字就会搜索一次,1秒内输入10下就会有10个请求发出去:
造成这个问题的原因是什么呢
是因为AutoComplete
中的onChange的事件,当每次改变的时候都会创建一个debounce
方法,所以onChange
事件触发10次,就会有10个防抖函数并执行。
如何解决
我们可以使用useCallback
将防抖方法进行缓存,避免不必要的刷新重建就可以了。
如下所示,useCallback
有两个参数:
useCallback(fn, deps)`
fn:要缓存的函数,在初始渲染期间,React将返回你的函数。在下一次渲染时,如果依赖关系(也就是deps)自上次渲染以来没有改变,React将再次给你相同的函数。
deps: 是一个数组,当里面的元素发生改变的时候会重新渲染函数。
因为,我们结合useCallback
这里我们将batchSearchDebounce
做一下修改即可:
const batchSearchDebounce = useCallback(
debounce(async (keyWords: string) => {
if (keyWords) {
batchData.onChange(keyWords);
return await searchComStdMod({ search: keyWords }).then((res) => {
const data: any[] = [];
res.data.forEach((item: any) => {
data.push({
label: (
<>
<Tag color="blue">公共模块</Tag>
{item.label}
</>
),
value: item.value,
name: item.label,
});
});
setSearchData(data);
});
}
}, 200),
[],
);
使用useCallback
之后就不会出现上述的问题了,防抖方法也就生效了:
useCallback 它可以让你在重新渲染之间缓存函数,避免函数冗余的重新渲染,以此来提高性能,没想到因为这样的机制也能解决掉一些逻辑问题。