IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 使用debounce在react函数组件中做防抖 -> 正文阅读

[JavaScript知识库]使用debounce在react函数组件中做防抖

使用debounce在react函数组件中做防抖

背景

在项目中有这样的常见需求:

? 提供一个搜索输入框,输入框需要响应用户的输入并且不需要用户点击搜索按钮直接发起搜索。

思路

1. 普通实现

其实这样的需求很常见,也并不难,第一反应就是监听用户输入,只要用户有输入操作立即拿到用户输入的数据直接发起搜索查询

  • 优点:响应迅速,并且确实可以达到需求的效果。

  • 缺点:发送多洗无效请求,浪费服务器的性能资源,增加服务器负担。

2. 防抖函数

使用防抖函数(debounce或者lodash.debounce

  • 优点:同样能做出迅速响应,并且能有效去除不必要的请求发送,节约服务器资源性能。

实现:

我手首先是将搜索框封装为一个组件MatSearchInput

import { Icon, InputBaseProps, StandardTextFieldProps, styled, TextField } from "@mui/material";
import { t } from "i18next";
import { memo } from "react";

const SytledInput = styled(TextField)(({ theme }) => {
  return {
    "& .MuiOutlinedInput-root": {
      "& fieldset": {
        borderColor: theme.palette.primary.main,
      },
    },
    "&:hover fieldset": {
      borderColor: theme.palette.primary.main + " !important",
    },
  };
});

interface MatSearchInputProps extends StandardTextFieldProps {
  placeholder?: string;
  // onChange?(): void;
  width?: number;
  inputProps?: InputBaseProps["inputProps"];
}

export default memo(function MatSearchInput(props: MatSearchInputProps) {
  return (
    <SytledInput
      {...props}
      style={{ width: props.width || 250 }}
      InputProps={{
        startAdornment: (
          <Icon sx={{ mr: 1 }} color="primary">
            search
          </Icon>
        ),
      }}
      placeholder={props.placeholder ? t(props.placeholder) : "Search..."}
      color="primary"
      size="small"
    ></SytledInput>
  );
});

然后在父级组件中监听input事件:

  const fetchTemplate = useCallback(() => {
    setLoading(true);
    fetchData(new PageLink(page, pageSize, searchValue, sortProp, "desc")).then((res) => {
      setTemplateData(res);
      setLoading(false);
    });
  }, [page, sortProp, searchValue]);

  useEffect(() => {
    fetchTemplate();
  }, [fetchTemplate]);

...
	<MatSearchInput value={searchValue} onChange={(e) => setSearchValue(e.target.value)} sx={{ ml: 2 }}></MatSearchInput>
...

在这里fetchTemplate为获取数据的方法,使用useCallback进行缓存,也就是说当依赖searchValue发生变化是该方法会重新计算,然后被useEffect监听到并且调用。

然而现在的问题就是输入框组件只要触发onChange事件就会直接引起请求发送,这是我们可以优化的点。

这里就用到了debounce(或者lodash库的debounce方法)

3. 遇到的坑

最开始的实现思路是在输入框组件中用useState维护自己的value和onChange事件,这样用户输入的时候输入框才能正常工作而不会卡顿,然后在自己的onChange事件中触发props传来的onChange,代码如下:

export default memo(function MatSearchInput(props: MatSearchInputProps) {
  const [value, setValue] = useState(props.value);
  const onChange = (event: ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value);
    emitChangeByProps(event);
  };

  const emitChangeByProps = debounce(function (event: ChangeEvent<HTMLInputElement>) {
    props.onChange(event);
  }, 300);

  return (
    <SytledInput
      {...props}
      style={{ width: props.width || 250 }}
      InputProps={{
        startAdornment: (
          <Icon sx={{ mr: 1 }} color="primary">
            search
          </Icon>
        ),
      }}
      placeholder={props.placeholder ? t(props.placeholder) : "Search..."}
      color="primary"
      size="small"
      value={value}
      onChange={onChange}
    ></SytledInput>
  );
});

运行后没有预想中的效果

原因

思考后发现原因应该是之前说过的性能优化的问题,根据react函数式组件的特性,当useState存储的值value发生变化的时候,组件会重新渲染,因此emitChangeByProps方法没次都是一个新的方法

解决

解决思路就是用useMemo将emitChangeByProps方法缓存起来,这样就可以实现了:

export default memo(function MatSearchInput(props: MatSearchInputProps) {
  const [value, setValue] = useState(props.value);
  const onChange = (event: ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value);
    emitChangeByProps(event);
  };

  // 这里用debounce做防抖  做性能优化
  const emitChangeByProps = useMemo(() => {
    return debounce(function (event: ChangeEvent<HTMLInputElement>) {
      props.onChange(event);
    }, 300);
  }, [props]);

  return (
    <SytledInput
      {...props}
      style={{ width: props.width || 250 }}
      InputProps={{
        startAdornment: (
          <Icon sx={{ mr: 1 }} color="primary">
            search
          </Icon>
        ),
      }}
      placeholder={props.placeholder ? t(props.placeholder) : "Search..."}
      color="primary"
      size="small"
      value={value}
      onChange={onChange}
    ></SytledInput>
  );
});
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 22:02:04  更:2022-03-17 22:05:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:50:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码