表格无限请求竟然是这个原因

本文描述了前端开发中遇到的问题,即表格快速点击分页器引发的循环请求。作者通过代码分析发现,数据结构设计不合理导致了请求参数混乱。提供了两种解决方案:一是记录并比较请求时间,二是分离数据结构,确保请求只在必要时触发。
摘要由CSDN通过智能技术生成

问题复现

起因:和后端同事联调需求功能时,发现历史页面的一个表格点击出现了循环请求,可能会直接打挂后端数据接口,后端同事直呼害怕,差点要提枪到我的工位直接爆头了😨

问题:表格展示列表数据,快速点击表格的分页器页码多次请求时,会出现循环请求的情况。

效果图:

代码分析

一看情况不对,立马去仔细分析了下这段代码逻辑,看一下循环请求的罪魁祸首。

第一反应肯定是先找到代码中发送请求的地方简单瞅一眼, 定位到是这个函数getTableData;请求的逻辑也比较简单:

  1. 重置页面的loadingerror状态,用来给加载期间给表格展示加载态和失败态

  2. 获取请求的参数配置,例如页码,开始、结束时间,排序号等

  3. 发送页面请求

  4. 等页面请求回来后,重新赋值表格数据以重新渲染页面

 const getTableData = async () => {
    try {
      setTableConfig({
        ...tableConfig,
        tableLoading: true,
        tableError: false,
      });
      const { pageNum, pageSize, sortInfo, createFrom, createTo } = tableConfig;
      const reqData: GetOvertimeValidListRequest = {
        pageNum,
        pageSize,
        createFrom,
        createTo,
        sortInfo,
        configurable,
        ruleID: ruleID || '',
      };
      // xxx省略逻辑代码
      const {
        total: totalData,
        definitionList: tableDefinitionList,
      } = await APIS.getOvertimeValidList(reqData);
      const tableData = formatTableData(tableDefinitionList);
      setTableConfig({
        ...tableConfig,
        tableData,
        total: totalData,
        tableLoading: false,
      });
      // xxx省略逻辑代码
    } catch (error) {
      setTableConfig({
        ...tableConfig,
        tableLoading: false,
        tableError: true,
      });
      logger.error('create overtime rule', 'getTableData error');
    }
  };




到此为此,不知道各位看官是否已经看出了问题!

由于当时时间紧迫,再晚几分钟后端大佬就要提刀相见了,我只能先输出下日志看下了,不知道大佬们都是用什么方式调试的,我直接原始办法直接加log输出;

至此,发现了问题的所在;

在多次点击页码的时候,每次请求会修改页码,但是上一次的请求没有回来的时候立马点击下次请求,导致下一次请求回来的时候页码又是旧的,effect的依赖项发生了改变又导致了重新发请求,以此不断循环请求。。。

举个栗子🌰:

  1. 点击页码2,发送请求前改变页码为2,再发送请求

  2. 点击页码3,发送请求前将页码2改为3,再发送请求

  3. 问题出现了,此时页码2的请求回来了,将页码3改为2,依赖项检测到改变又会重新请求页码2

  4. 然后页码3的请求回来了,将页码2改为3,依赖项检测到改变又会重新请求页码3

  5. 然后就是动词打次了。。。

所以是这个页面的参数出了问题,立马去看了下数据结构,果然是所有的页面参数都放在一起;

export type TableConfig = {
  total: number;
  pageSize: number;
  pageNum: number;
  tableData: TableData[];
  sortInfo: SortInfo;
  tableLoading: boolean;
  tableError: boolean;
  createFrom: number;
  createTo: number;
};

页面请求effect:

 useEffect(() => {
    getTableData();
  }, [    
    tableConfig.pageNum,    
    tableConfig.pageSize,    
    tableConfig.sortInfo,    
    tableConfig.createFrom,   
    tableConfig.createTo,   
    definitionID,  
]);

解法1

但是问题还得解啊,可不能把后端接口打炸了。所以小脑袋一转,先想了一个比较hack的解法。就是去记录最后一次的请求时间,每次请求都去重置下时间,等到请求返回的时候,去比较:

如果当前时间是小于最后一次记录的时间,那么说明这次的请求并不是最后一次,那么直接舍弃不去更新表单数据,那么就不会修改到页码了;

如果当前是最后一次记录的时间,那么这个请求就是可以作为表单数据的更新的;

 // 记录最后一次请求的时间,防止多次触发导致循环请求
 const lastRequestTime = useRef ( Date . now ());
 
 const getTableData = async () => {
    try {
   const currentRequestTime = Date . now ();
lastRequestTime. current = currentRequestTime;
      setTableConfig({
        ...tableConfig,
        tableLoading: true,
        tableError: false,
      });
      const { pageNum, pageSize, sortInfo, createFrom, createTo } = tableConfig;
      const reqData: GetOvertimeValidListRequest = {
        pageNum,
        pageSize,
        createFrom,
        createTo,
        sortInfo,
        configurable,
        ruleID: ruleID || '',
      };
      // xxx省略逻辑代码
      const {
        total: totalData,
        definitionList: tableDefinitionList,
      } = await APIS.getOvertimeValidList(reqData);
      const tableData = formatTableData(tableDefinitionList);
      if (currentRequestTime < lastRequestTime. current ) return ;
      setTableConfig({
        ...tableConfig,
        tableData,
        total: totalData,
        tableLoading: false,
      });
      // xxx省略逻辑代码
    } catch (error) {
      setTableConfig({
        ...tableConfig,
        tableLoading: false,
        tableError: true,
      });
      logger.error('create overtime rule', 'getTableData error');
    }
  };

解法2

下班美美回家,躺在床上思考了一番;由于问题的根本是数据结构设计的不太合理,页面的数据和页面的请求参数混在一起了,导致循环请求了页面的数据,所以还是得合理的拆分一下数据结构才行!

所以把页面数据类的参数和页面请求类的参数分开来:

 
export type  TableConfig = {
  total: number;
  tableData: TableData[];
  tableLoading: boolean;
  tableError: boolean;
};

export type RequestConfig  = {
  pageSize: number;
  pageNum: number;
  sortInfo: SortInfo;
  createFrom: number;
  createTo: number;
}

那么大功告成,只有页面请求的参数发生改变时才会重新请求数据,数据请求回来的时候只会修改页面的数据,井水不犯河水,直接来了一个楚河汉界。

useEffect(() => {
    getTableData();
}, RequestConfig]);
  
const getTableData = async () => {
    const {
        total: totalData,
        definitionList: tableDefinitionList,
     } = await APIS.getOvertimeValidList(reqData);
     const tableData = formatTableData(tableDefinitionList);
      setTableConfig({
        ...tableConfig,
        tableData,
        total: totalData,
        tableLoading: false,
      });
  }


至此,算是先暂时解决了问题,不知道各位看官姥爷有没有好的建议~

总结

问题复现:一个表格普通点击出现了循环请求,可能会直接打挂后端数据接口。经分析,问题的根本原因是数据结构设计不合理,导致页面的数据和页面请求参数混淆在一起,从而循环请求了页面数据。

解决方案1:记录最后一次请求的时间,每次请求都去重置时间,等到请求返回时,比较当前时间是否小于最后一次记录的时间,如果是,则舍弃更新表单数据,避免修改页码。但此方案较为复杂,需要额外的逻辑处理。

解决方案2:将页面数据类的参数和页面请求类的参数分开,使得页面请求的参数发生改变时才会重新请求数据,数据请求回来时只会修改页面的数据。此方案更为简洁,只需要合理拆分数据结构即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值