业务背景

我们有一个需求是在用户输入用例名称的时候,系统根据名称去匹配公共用例库中的用例模块展示在下拉框中,然后用户可选择想要的模块导入其中的用例:

antd renderFormItem AutoComplete结合防抖导致防抖失效的解决办法-LMLPHP
但是如果用户每输入一个字符就去调用接口查询的话,这样就太频繁了,会给服务端造成不小的压力,所以就需要使用防抖方法来进行限流控制。这里的方案如下:
因为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个请求发出去:
antd renderFormItem AutoComplete结合防抖导致防抖失效的解决办法-LMLPHP

造成这个问题的原因是什么呢

是因为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之后就不会出现上述的问题了,防抖方法也就生效了:

antd renderFormItem AutoComplete结合防抖导致防抖失效的解决办法-LMLPHP
useCallback 它可以让你在重新渲染之间缓存函数,避免函数冗余的重新渲染,以此来提高性能,没想到因为这样的机制也能解决掉一些逻辑问题。

11-01 10:36