笔记四:性能优化与调试技巧:React中的防抖技术

200 阅读2分钟

防抖(debounce)即当事件触发时,对应的函数不会被立即触发,而是会推迟执行,当事件密集连续出发时,只有当设定的延迟时间后没有再次触发该事件,函数才会被触发。在输入框频繁输入内容/搜索/提交,频繁点击按钮,滚动,缩放等场景有着广泛的应用。

显然对于搜索框的API调用来说,防抖是很有必要的,因此本项目也应用了搜索框的防抖技巧。自己实现防抖固然可行,但如果有可用的库更方便快捷,所幸lodash提供了debounce函数,使用也较为便捷,但…在此基础之上还有什么改进空间吗?

前提 - React-autosuggest的Handler:

使用React-autosuggest实现搜索功能,需要获取搜索内容并设置react状态,并返回最后的jsx/tsx,这也是我们需要添加防抖功能的地方 - API的调用。

const suggestionsFetchRequestedHandler = async ({value}: {value: string;}) => {
		...
    try {
      const newSuggestions = await fetchPackageNames(trimmedInputValue);
      setSuggestions(newSuggestions);
			...
    }
  };

第一次尝试 - 原生的lodash内置debounce函数

我们在这里使用debounce函数100毫秒后触发搜索栏的suggestionsFetchRequestedHandler函数

const debouncedSuggestionsFetch = debounce(suggestionsFetchRequestedHandler, 100);
async function handleSuggestion(e: React.ChangeEvent<HTMLInputElement>) {
  debouncedSuggestionsFetch (e.target.value);
}

防抖在每次事件触发时被激活,可以满足需求,但每次事件变化时都会触发重新渲染,显然存在性能更好的方式。

第二次尝试 - 结合react.js的useRef

const debouncedSuggestionsFetch = useRef(
    debounce(suggestionsFetchRequestedHandler, 100),
  ).current;

使用useRef,我们可以将debounce函数储存起来用于不同的渲染过程中,避免每次都重新渲染,可以提升性能。

第三次完善 - 清除防抖函数

当组件卸载时,API请求有可能仍在执行,这种情况下设置状态就会出错,我们可以使用useEffect检测防抖函数的side effect并利用API提供的.cancel函数清除防抖函数,代码如下:

useEffect(() => {
    return () => {
      debouncedSuggestionsFetch.cancel();
    };
  }, [debouncedSuggestionsFetch]);

return (
      <Autosuggest
				...
        onSuggestionsFetchRequested={debouncedSuggestionsFetch}
				...
      />
  );

总结:

即使我们使用了现有的库函数,在应用于不同框架时,也需要考虑适用场景,可能存在的问题以及性能优化的可能性。