如何修复react-hooks/exhaustive-deps错误并优化分页实现?
问题
遇到了react-hooks/exhaustive-deps的lint错误警告,代码如下:
const fetchData = async pageNumber => { if (pageNumber > totalPages) { return; } try { setIsLoading(true); // ... call api const content = response.data.content; if (pageNumber === 1) { setTotalPages(response.data.totalPages); setActivityData(content); } else { setActivityData([...activityData, ...content]); } setCurrPage(currPage + 1); } catch (e) { console.log(e); } finally { // ... } }; useEffect(() => { fetchData(currPage); // eslint-disable-next-line react-hooks/exhaustive-deps }, [currPage]); useEffect(() => { setCurrPage(1); setActivityData([]); setTotalPages(1); }, [activityStatus]);
这段代码用于分页展示activityData。如果把fetchData加入useEffect依赖,会触发函数重渲染相关错误;尝试过把fetchData放入useEffect但无法实现,现咨询:
- 当前的
useEffect用法存在什么问题? - 有没有更好的分页管理与展示方式?
回答
1. 当前useEffect用法的问题
- 用
eslint-disable跳过依赖检查本质是回避核心问题:fetchData依赖组件内部的activityData、currPage、totalPages等state,每次组件渲染时fetchData都会被重新创建。如果把它加入useEffect依赖,会导致useEffect在每次渲染时都触发,进而调用fetchData更新state,最终引发无限循环。 fetchData直接使用state的当前值存在闭包陷阱:比如当activityData更新后,fetchData里捕获的还是旧的activityData值,拼接数据时会出现错误;同理currPage的更新也可能基于旧值,导致页码计算偏差。
2. 更好的分页管理与展示方式
方案一:useCallback包裹fetchData+ 函数式更新state
用useCallback缓存fetchData,避免每次渲染都重新创建;同时用函数式更新state,获取最新的state值,解决闭包问题:
const fetchData = useCallback(async (pageNumber) => { if (pageNumber > totalPages) return; try { setIsLoading(true); // ... call api const content = response.data.content; if (pageNumber === 1) { setTotalPages(response.data.totalPages); setActivityData(content); } else { // 函数式更新,获取最新的activityData setActivityData(prev => [...prev, ...content]); } // 函数式更新currPage setCurrPage(prev => prev + 1); } catch (e) { console.log(e); } finally { setIsLoading(false); } }, [totalPages]); // 只依赖totalPages,其他state用函数式更新获取 useEffect(() => { fetchData(currPage); }, [currPage, fetchData]); // 现在可以安全加入fetchData依赖
方案二:将fetchData逻辑移入useEffect内部
如果不需要复用fetchData,直接把逻辑放进useEffect,完全避免函数依赖问题:
useEffect(() => { const fetchData = async () => { if (currPage > totalPages) return; try { setIsLoading(true); // ... call api const content = response.data.content; if (currPage === 1) { setTotalPages(response.data.totalPages); setActivityData(content); } else { setActivityData(prev => [...prev, ...content]); } setCurrPage(prev => prev + 1); } catch (e) { console.log(e); } finally { setIsLoading(false); } }; fetchData(); }, [currPage, totalPages]); // 只依赖必要的state
方案三:自定义Hook封装分页逻辑
把分页相关的state和逻辑封装成自定义Hook,提升复用性和代码整洁度:
const usePagedActivity = (activityStatus) => { const [activityData, setActivityData] = useState([]); const [currPage, setCurrPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [isLoading, setIsLoading] = useState(false); const fetchData = useCallback(async (pageNumber) => { if (pageNumber > totalPages) return; setIsLoading(true); try { // ... 根据activityStatus调用api const response = await api.getActivity({ page: pageNumber, status: activityStatus }); const content = response.data.content; if (pageNumber === 1) { setTotalPages(response.data.totalPages); setActivityData(content); } else { setActivityData(prev => [...prev, ...content]); } setCurrPage(prev => prev + 1); } catch (e) { console.log(e); } finally { setIsLoading(false); } }, [activityStatus, totalPages]); // 切换状态时重置分页 useEffect(() => { setCurrPage(1); setActivityData([]); setTotalPages(1); }, [activityStatus]); // 页码变化时请求数据 useEffect(() => { if (currPage === 1 || currPage <= totalPages) { fetchData(currPage); } }, [currPage, fetchData]); return { activityData, currPage, totalPages, isLoading }; }; // 在组件中使用 const { activityData, isLoading } = usePagedActivity(activityStatus);
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

