业务背景
我们有一个需求是在用户输入用例名称的时候,系统根据名称去匹配公共用例库中的用例模块展示在下拉框中,然后用户可选择想要的模块导入其中的用例:
但是如果用户每输入一个字符就去调用接口查询的话,这样就太频繁了,会给服务端造成不小的压力,所以就需要使用防抖方法来进行限流控制。这里的方案如下:
因为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 它可以让你在重新渲染之间缓存函数,避免函数冗余的重新渲染,以此来提高性能,没想到因为这样的机制也能解决掉一些逻辑问题。