当前位置: 首页 > news >正文

前端Tabs切换导致的数据问题

文章目录

  • 1、使用唯一标识
  • 2、使用防抖
  • 3、在组件卸载时取消请求
  • 4、使用 Map 缓存每个 tab 的数据
  • 5、禁用disabled
  • 5、初始化获取所有tabs数据

tabs切换导致的数据请求,有可能会导致请求频率高、返回值不对应的问题,提供几种基本的方案。

1、使用唯一标识

为每次请求生成一个唯一标识(如 UUID),并在响应时检查该标识是否仍然有效。如果请求的标识不是最新的,则忽略该请求的结果。

import { useRef } from 'react';const AppDetail = () => {const [activeTab, setActiveTab] = useState<number>(0);const [dataSource, setDataSource] = useState([]);const [loading, setLoading] = useState(false);const requestRef = useRef<string | null>(null);const fetchData = async (tabKey: number) => {setLoading(true);const currentRequestId = `${tabKey}-${Date.now()}`; // 生成唯一标识requestRef.current = currentRequestId;try {const response = await fetch(`/api/data?tab=${tabKey}`);const data = await response.json();// 检查请求是否仍然有效if (requestRef.current === currentRequestId) {setDataSource(data);}} catch (error) {console.error('Error fetching data:', error);} finally {if (requestRef.current === currentRequestId) {setLoading(false);}}};const handleTabChange = (key: string) => {setActiveTab(Number(key));fetchData(Number(key));};return (<Tabs activeKey={activeTab + ''} onChange={handleTabChange}>{/* Tabs 内容 */}</Tabs>);
};

2、使用防抖

通过防抖机制,限制用户快速切换 Tabs 时的请求频率,避免频繁发起请求。

import { debounce } from 'lodash';const AppDetail = () => {const [activeTab, setActiveTab] = useState<number>(0);const [dataSource, setDataSource] = useState([]);const [loading, setLoading] = useState(false);const fetchData = async (tabKey: number) => {setLoading(true);try {const response = await fetch(`/api/data?tab=${tabKey}`);const data = await response.json();setDataSource(data);} catch (error) {console.error('Error fetching data:', error);} finally {setLoading(false);}};const debouncedFetchData = useCallback(debounce(fetchData, 300), []);const handleTabChange = (key: string) => {setActiveTab(Number(key));debouncedFetchData(Number(key));};return (<Tabs activeKey={activeTab + ''} onChange={handleTabChange}>{/* Tabs 内容 */}</Tabs>);
};

3、在组件卸载时取消请求

  • 如果组件在请求完成前被卸载,可能会导致状态更新错误。可以通过 AbortController 或 isMounted 标志来取消未完成的请求。
  • AbortController 是在同一个接口请求时取消之前的请求,可以封装在请求函数里,这里只做模拟展示(isCanceler)
import { useEffect, useRef } from 'react';const AppDetail = () => {const [activeTab, setActiveTab] = useState<number>(0);const [dataSource, setDataSource] = useState([]);const [loading, setLoading] = useState(false);const isMounted = useRef(true);useEffect(() => {isMounted.current = true;return () => {isMounted.current = false;};}, []);const fetchData = async (tabKey: number) => {setLoading(true);try {const [response, isCanceler] = await fetch(`/api/data?tab=${tabKey}`);const data = await response.json();if (isMounted.current && !isCanceler) {setDataSource(data);}} catch (error) {console.error('Error fetching data:', error);} finally {if (isMounted.current) {setLoading(false);}}};const handleTabChange = (key: string) => {setActiveTab(Number(key));fetchData(Number(key));};return (<Tabs activeKey={activeTab + ''} onChange={handleTabChange}>{/* Tabs 内容 */}</Tabs>);
};

4、使用 Map 缓存每个 tab 的数据

  • 通过 Map 数据结构缓存每个 tab 的数据,避免重复请求并解决 tab 切换时数据请求异常的问题。
  • 这里使用了缓存数据就会存在数据实时性的问题。
import React, { useState, useRef, useCallback } from 'react';
import { Tabs, Table, Spin } from 'antd';const fetchTabData = async (tabKey) => {// 模拟 API 请求return new Promise((resolve) => {setTimeout(() => {resolve({ data: [`Data for tab ${tabKey}`], key: tabKey });}, 1000);});
};const TabsContainer = () => {const [activeTab, setActiveTab] = useState('tab1'); // 当前激活的 tabconst [loading, setLoading] = useState(false); // 加载状态const [dataSource, setDataSource] = useState([]); // 当前 tab 的数据const dataCache = useRef(new Map()); // 使用 Map 缓存每个 tab 的数据// 获取数据const getTabData = useCallback(async (tabKey) => {// 如果缓存中有数据,直接使用缓存数据if (dataCache.current.has(tabKey)) {setDataSource(dataCache.current.get(tabKey));return;}// 如果缓存中没有数据,发起请求setLoading(true);try {const response = await fetchTabData(tabKey);const data = response.data;// 更新缓存dataCache.current.set(tabKey, data);// 更新当前数据setDataSource(data);} catch (error) {console.error(`Error fetching data for tab ${tabKey}:`, error);} finally {setLoading(false);}},[],);// 处理 tab 切换const handleTabChange = (key) => {setActiveTab(key);getTabData(key); // 切换 tab 时获取数据};return (<div><Tabs activeKey={activeTab} onChange={handleTabChange}><Tabs.TabPane tab="Tab 1" key="tab1" /><Tabs.TabPane tab="Tab 2" key="tab2" /><Tabs.TabPane tab="Tab 3" key="tab3" /></Tabs><Spin spinning={loading}><TablerowKey={(record) => record}dataSource={dataSource}columns={[{ title: 'Data', dataIndex: 'data', key: 'data' }]}pagination={false}/></Spin></div>);
};export default TabsContainer;

5、禁用disabled

简单有效,通过在请求数据时禁用整个 Tabs,可以有效避免用户在请求未完成时切换 Tabs。

import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Tabs, Table, Spin } from 'antd';const fetchTabData = async (tabKey) => {// 模拟 API 请求return new Promise((resolve) => {setTimeout(() => {resolve({ data: [`Data for tab ${tabKey}`], key: tabKey });}, 1000);});
};const AppDetail = () => {const [activeTab, setActiveTab] = useState('tab1'); // 当前激活的 tabconst [loading, setLoading] = useState(false); // 加载状态const [dataSource, setDataSource] = useState([]); // 当前 tab 的数据// 获取数据const getTabData = useCallback(async (tabKey) => {setLoading(true); // 开始加载,禁用 Tabstry {const response = await fetchTabData(tabKey);const data = response.data;// 更新当前数据setDataSource(data);} catch (error) {console.error(`Error fetching data for tab ${tabKey}:`, error);} finally {setLoading(false); // 加载完成,启用 Tabs}}, []);// 处理 tab 切换const handleTabChange = (key) => {setActiveTab(key);getTabData(key); // 切换 tab 时获取数据};return (<div>{/* Tabs 区域 */}<Spin spinning={loading}><TabsactiveKey={activeTab}onChange={handleTabChange}tabBarStyle={loading ? { pointerEvents: 'none', opacity: 0.5 } : {}}><Tabs.TabPane tab="Tab 1" key="tab1" /><Tabs.TabPane tab="Tab 2" key="tab2" /><Tabs.TabPane tab="Tab 3" key="tab3" /></Tabs></Spin>{/* 表格区域 */}<TablerowKey={(record) => record}dataSource={dataSource}columns={[{ title: 'Data', dataIndex: 'data', key: 'data' }]}pagination={false}/></div>);
};export default AppDetail;

5、初始化获取所有tabs数据

  • 组件加载时默认请求所有 Tabs 的数据并缓存起来,切换 Tabs 时只切换数据,而不发起新的请求
  • 如果接口返回的就是所有tabs的数据,那一次请求即可,适合数据量小的情况;
  • 对于初始化一次就不再更新的数据,一般属于固定的常量数据、实时性低的数据。
import React, { useState, useEffect, useMemo } from 'react';
import { Tabs, Table, Spin } from 'antd';// 模拟 API 请求
const fetchTabData = async (tabKey: string) => {return new Promise((resolve) => {setTimeout(() => {resolve(Array.from({ length: 5 }, (_, index) => ({id: `${tabKey}-${index}`,name: `Item ${index + 1} for ${tabKey}`,})),);}, 1000);});
};// Tabs 配置
const getTabsConfig = () => [{ key: 'tab1', label: 'Tab 1' },{ key: 'tab2', label: 'Tab 2' },{ key: 'tab3', label: 'Tab 3' },
];const App = () => {const [activeTab, setActiveTab] = useState<string>(null); // 当前激活的 Tabconst [loading, setLoading] = useState<boolean>(true); // 全局加载状态const [dataCache, setDataCache] = useState<Record<string, any[]>>({}); // 缓存所有 Tab 的数据const tabs = useMemo(() => getTabsConfig(), []); // 获取 Tabs 配置// 默认请求所有 Tabs 的数据useEffect(() => {const fetchAllTabsData = async () => {setLoading(true);const cache: Record<string, any[]> = {};try {// 并发请求所有 Tabs 的数据const requests = tabs.map((tab) =>fetchTabData(tab.key).then((data) => {cache[tab.key] = data;}),);await Promise.all(requests); // 等待所有请求完成setDataCache(cache); // 缓存所有数据setActiveTab(tabs[0]?.key); // 默认激活第一个 Tab} catch (error) {console.error('Error fetching tabs data:', error);} finally {setLoading(false);}};fetchAllTabsData();}, [tabs]);// 切换 Tabconst handleTabChange = (key: string) => {setActiveTab(key);};return (<div style={{ padding: 20 }}>{/* 全局加载指示器 */}<Spin spinning={loading}>{/* Tabs 区域 */}<Tabs activeKey={activeTab} onChange={handleTabChange}>{tabs.map((tab) => (<Tabs.TabPane tab={tab.label} key={tab.key} />))}</Tabs>{/* 表格区域 */}{activeTab && (<TablerowKey={(record) => record.id}dataSource={dataCache[activeTab] || []} // 从缓存中读取数据columns={[{ title: 'ID', dataIndex: 'id', key: 'id' },{ title: 'Name', dataIndex: 'name', key: 'name' },]}pagination={false}/>)}</Spin></div>);
};export default App;
http://www.dtcms.com/a/566612.html

相关文章:

  • 中专旅游管理专业职业发展指南
  • 微网站管理平台wordpress 主题 最简单
  • 彩票网站是怎么做的南宁做网站开发的公司有哪些
  • 网站为什么开发appc mvc制作网站开发
  • 做服装招聘的网站有哪些群辉怎么做视频网站
  • 佛山顺德容桂网站制作asp网站建设案例
  • 解决Grid布局下el-table自适应缩小失败的问题
  • 企业做网站应该注意的问题北京排名seo
  • 基础展示营销型型网站网站建设中 英语
  • Javascript运算符之一元运算符
  • 留言板网站模板editplus建设网站教学
  • 网站设计公司域名服务器建设wordpress4.5.3
  • 《投资-150》股市不同的操作类型、对于的操作手法、盈利方式、对个股的影响
  • 三轴云台之闭环控制技术
  • 做企业宣传网站dw建设网站的代码模板下载
  • 【EmberTrace AI】多智能体协作平台产品研发进度……
  • 河南建设监理协会网站电话朋友圈推广怎么收费
  • 东莞专业拍摄做网站照片重庆旅游攻略详细安排
  • 从 OpenSearch 到 Apache Doris:领创集团日志系统升级实践,降本 45%
  • InvSR:Arbitrary-steps Image Super-resolution via Diffusion Inversion
  • 广东省省考备考(第一百四十天11.3)——数量关系、资料分析(强化训练)
  • 网站性能优化三明网站开发
  • 【动态规划:01背包】01背包详解 模板题 优化
  • 专门做餐饮空间设计的网站ui设计常用软件
  • 企业信息公示平台徐州seo
  • 雕塑网站模板电商网站设计的流程
  • RAE:Diffusion Transformers with Representation Autoencoders
  • 医院网站开发多少钱烟台网络公司员工人数
  • 算法学习记录11——Python 多变量赋值问题
  • 怎样拥有自己的网站外行学习个人网站建设