如何避免React中useEffect因快速翻页导致的Axios重复请求?
解决React分页快速切换时的状态波动与重复请求问题
问题根源
- 竞态条件:快速切换页码时,多个API请求并行处理,旧请求可能晚于新请求返回,会覆盖最新的交易数据和页码状态,导致页面显示错乱。
- 无限循环触发:请求回调中更新
paginationConfig时,使用了旧的状态对象展开,当返回的page与当前pageNo不一致时,会再次触发useEffect发起新请求,形成循环。
解决方案
1. 取消未完成请求
使用Axios的CancelToken,在页码变更或组件卸载时,取消上一次未完成的请求,避免旧请求干扰状态。
2. 优化状态更新逻辑
采用函数式状态更新获取最新状态,同时判断是否需要更新页码,避免不必要的useEffect触发。
修正后的完整代码
import { useEffect, useState } from 'react'; import axios from 'axios'; // 假设已定义的常量 const TRANSACTIONS_ENDPOINT = '/api/transactions'; const PAGE_STORAGE_KEY = 'currentPage'; function TransactionList() { const [transactions, setTransactions] = useState([]); const [paginationConfig, setPaginationConfig] = useState({ pageNo: 1, totalPages: 0 }); const [searchConfig, setSearchConfig] = useState({ search_by: '', search_value: '' }); const [loading, setLoading] = useState(false); useEffect(() => { // 创建新的取消令牌 const cancelSource = axios.CancelToken.source(); const fetchTransactions = async () => { setLoading(true); try { const response = await axios.get(TRANSACTIONS_ENDPOINT, { cancelToken: cancelSource.token, params: { page: paginationConfig.pageNo, search_by: searchConfig.search_by, search_value: searchConfig.search_value } }); const { transactions, total_pages, page } = response.data; setTransactions(transactions); // 函数式更新,仅当返回页码与当前页码不同时才更新 setPaginationConfig(prevState => { if (prevState.pageNo === page) return prevState; return { ...prevState, pageNo: page, totalPages: total_pages }; }); sessionStorage.setItem(PAGE_STORAGE_KEY, page.toString()); } catch (err) { // 忽略主动取消的请求错误 if (!axios.isCancel(err)) { console.error(err); } } finally { setLoading(false); } }; fetchTransactions(); // 组件卸载或依赖变更时取消请求 return () => { cancelSource.cancel('请求已取消'); }; }, [paginationConfig.pageNo, searchConfig.search_by, searchConfig.search_value]); // 分页切换处理函数示例 const handlePageChange = (newPage) => { setPaginationConfig(prev => ({ ...prev, pageNo: newPage })); }; return ( <div> {/* 交易表格渲染 */} {/* 分页导航组件 */} </div> ); } export default TransactionList;
关键优化点
- 请求取消机制:每次触发
useEffect时生成新的取消令牌,同时在清理函数中取消当前请求,确保只有最新的请求能完成并更新状态。 - 函数式状态更新:通过
setPaginationConfig(prevState => ...)获取最新状态,避免闭包捕获旧值的问题,同时增加页码判断,防止无意义的状态更新触发useEffect循环。 - 依赖数组细化:将
searchConfig的属性拆分为单独依赖,避免因对象引用变化导致的不必要请求。
内容的提问来源于stack exchange,提问作者enggPS
相关产品推荐
相关产品推荐

