React分页场景下如何避免一个useEffect触发另一个重复执行
React分页修改排序/每页条数时避免重复请求的解决方案
问题核心根源:现有代码将「排序参数变更后重置页码」和「参数变更触发数据请求」拆成了两个独立的useEffect,导致状态变更产生链式触发:排序参数修改后,第一个请求useEffect先触发执行,随后第二个重置页码的useEffect执行修改currentPage,再次触发请求useEffect,最终产生两次重复请求。
方案1:事件回调中同步重置页码(最推荐,符合React最佳实践)
优先不要用useEffect处理内部状态的同步逻辑,直接在触发排序、每页条数修改的用户交互回调中同步更新所有状态,React会将同一事件循环内的多次setState合并为单次渲染,只会触发一次数据请求。
示例代码调整如下:
import { useState, useEffect } from 'react'; export default function MyComponent() { const [items, setItems] = useState([]); const [column, setColumn] = useState(); const [direction, setDirection] = useState(); const [currentPage, setCurrentPage] = useState(1); const [perPage, setPerPage] = useState(10); // 数据请求useEffect保留,不用改 useEffect( () => (async () => { const response = await fetch('...'); const { items } = await response.json(); setItems(items); })(), [column, direction, currentPage, perPage] ); // 排序点击回调,直接同步更新所有状态 const handleSortChange = (newColumn, newDirection) => { setColumn(newColumn); setDirection(newDirection); setCurrentPage(1); // 和排序参数在同一次事件循环中更新,会被React合并状态 }; // 每页条数变更回调同理 const handlePerPageChange = (newPerPage) => { setPerPage(newPerPage); setCurrentPage(1); }; return ( // 模板中绑定对应的回调即可:比如表头点击触发handleSortChange,下拉选切换触发handlePerPageChange ); }
这个方案逻辑最直观,没有隐藏的副作用联动,性能也最优。
方案2:合并useEffect逻辑(适合状态不由本组件控制的场景)
如果排序、每页条数的状态是从父组件传入,或者来自其他非本组件交互的更新,没法在本组件的回调中处理重置逻辑,可以将两个useEffect合并,用useRef缓存上一次的参数做对比,避免重复请求:
import { useState, useEffect, useRef } from 'react'; export default function MyComponent() { const [items, setItems] = useState([]); const [column, setColumn] = useState(); const [direction, setDirection] = useState(); const [currentPage, setCurrentPage] = useState(1); const [perPage, setPerPage] = useState(10); // 缓存上一次的排序、每页条数参数 const prevFilterRef = useRef({ column, direction, perPage }); useEffect(() => { const fetchData = async () => { const response = await fetch('...'); const { items } = await response.json(); setItems(items); }; const isFilterChanged = prevFilterRef.current.column !== column || prevFilterRef.current.direction !== direction || prevFilterRef.current.perPage !== perPage; if (isFilterChanged) { // 过滤参数变化时先重置页码,更新缓存,直接触发一次请求即可 setCurrentPage(1); prevFilterRef.current = { column, direction, perPage }; fetchData(); return; } // 仅页码变更的场景,正常发请求 fetchData(); }, [column, direction, currentPage, perPage]); return ( // 模板代码 ); }
两种方案都可以彻底避免重复请求的问题,优先选方案1即可。
内容的提问来源于stack exchange,提问作者Robo Robok
相关产品推荐
相关产品推荐

