React如何忽略旧请求 仅处理最后一次API请求的返回数据
React Table 多筛选条件请求竞态问题解决方案
核心问题为异步请求竞态:短时间内发起的多个请求响应返回顺序和发起顺序不一致,导致旧请求的晚到响应覆盖最新数据,出现表格数据跳动。
方案1:使用AbortController取消过期请求(推荐)
原生浏览器API支持,直接取消未完成的过期请求,既避免旧响应处理,也减少无效的服务器资源占用和浏览器请求队列消耗。
import { useRef, useState, useEffect } from 'react'; function FilterTable() { const [tableData, setTableData] = useState([]); const [loading, setLoading] = useState(false); // 存储当前请求的取消控制器实例 const abortCtrlRef = useRef(null); const fetchData = async (filters) => { // 发起新请求前先取消上一次未完成的请求 if (abortCtrlRef.current) { abortCtrlRef.current.abort(); } const currentAbortCtrl = new AbortController(); abortCtrlRef.current = currentAbortCtrl; try { setLoading(true); const response = await fetch('/api/getTableData', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(filters), signal: currentAbortCtrl.signal }); const result = await response.json(); // 仅当前请求未被取消时才更新数据 if (!currentAbortCtrl.signal.aborted) { setTableData(result); } } catch (error) { // 忽略主动取消请求产生的错误 if (error.name !== 'AbortError') { console.error('数据请求失败:', error); } } finally { // 仅当前请求未被取消时才关闭加载状态 if (!currentAbortCtrl.signal.aborted) { setLoading(false); } } }; // 筛选条件变化时触发请求,filters为你的筛选、排序、搜索参数集合 useEffect(() => { fetchData(filters); // 组件卸载时取消未完成的请求,避免内存泄漏 return () => abortCtrlRef.current?.abort(); }, [filters]); // 表格渲染逻辑... }
方案2:请求ID标记法(兼容无取消能力的请求场景)
如果你的请求库不支持取消操作,可以通过标记请求ID的方式过滤旧请求的响应,仅处理最新发起的请求返回结果。
import { useRef, useState, useEffect } from 'react'; function FilterTable() { const [tableData, setTableData] = useState([]); const [loading, setLoading] = useState(false); // 存储最新一次请求的ID const latestReqIdRef = useRef(0); const fetchData = async (filters) => { const currentReqId = latestReqIdRef.current + 1; latestReqIdRef.current = currentReqId; try { setLoading(true); const response = await fetch('/api/getTableData', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(filters) }); const result = await response.json(); // 仅当前请求是最新发起的请求时才更新数据 if (currentReqId === latestReqIdRef.current) { setTableData(result); setLoading(false); } } catch (error) { // 仅处理最新请求的报错 if (currentReqId === latestReqIdRef.current) { console.error('数据请求失败:', error); setLoading(false); } } }; useEffect(() => { fetchData(filters); }, [filters]); // 表格渲染逻辑... }
额外优化建议
如果是搜索输入这类高频触发的操作,可以搭配防抖(debounce)逻辑,比如用户输入停止300ms后再发起请求,从源头减少不必要的并发请求,进一步提升体验。
内容的提问来源于stack exchange,提问作者Karam Jabareen
相关产品推荐
相关产品推荐

