React实现无限滚动时useEffect出现依赖相关报错如何解决
问题根因
- 第一个报错是
react-hooks/exhaustive-deps语法规则检测到useEffect中用到了props.category和updateNews但未加入依赖数组,属于规范类告警。 - 第二个报错是因为
updateNews是组件内部定义的普通函数,每次组件重新渲染都会生成新的函数引用,放到useEffect依赖中会导致useEffect被无意义重复触发,存在死循环风险。
修复方案(推荐直接使用方案二,逻辑更简洁无额外钩子依赖)
方案1:用useCallback缓存updateNews函数
- 从react包中导入
useCallback钩子 - 用
useCallback包裹updateNews的定义,同时将状态更新改为函数式写法,避免依赖组件内部state,减少不必要的依赖项 - 将缓存后的
updateNews加入useEffect的依赖数组即可
方案2:直接将updateNews逻辑移到useEffect内部
把updateNews的实现直接写到useEffect回调里,无需额外处理函数引用变化问题,同时修正状态更新的闭包风险,修改后完整代码如下:
import React, { useEffect, useState } from "react"; import NewsItem from "./NewsItem"; import Spinner from "./Spinner"; import PropTypes from "prop-types"; import InfiniteScroll from "react-infinite-scroll-component"; const News = (props) => { const [articles, setArticles] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [totalResults, setTotalResults] = useState(0); const capitalizeFirstLetter = (string) => { return string.charAt(0).toUpperCase() + string.slice(1); }; useEffect(() => { // 把updateNews逻辑直接移到useEffect内部,避免函数引用变化问题 const updateNews = async () => { props.setProgress(10); const url = `https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${page}&pageSize=${props.pageSize}`; props.setProgress(30); let data = await fetch(url); props.setProgress(50); let parsedData = await data.json(); props.setProgress(70); if (parsedData) { // 用函数式更新,不需要依赖外部的articles变量,避免闭包问题 setArticles(prevArticles => prevArticles.concat(parsedData.articles)); setLoading(false); setTotalResults(parsedData.totalResults); } props.setProgress(100); }; updateNews(); document.title = `${capitalizeFirstLetter(props.category)} - NewsMonkey`; // 补全所有用到的依赖,不会出现遗漏告警也不会重复触发 }, [page, props.category, props.country, props.apiKey, props.pageSize, props.setProgress]); // 改成函数式更新,避免闭包导致页码更新错误 const fetchMoreData = () => { setPage(prevPage => prevPage + 1); }; return ( <> <h3 className="text-center" style={{ marginTop: "4%" }}> NewsMonkey - Top {`${capitalizeFirstLetter(props.category)}`} Headlines </h3> {loading && <Spinner />} <InfiniteScroll dataLength={articles.length} next={fetchMoreData} hasMore={articles.length < totalResults} loader={<Spinner />} > <div className="container"> <div className="row"> {articles.map((element) => { return ( <div className="col-md-4" key={element.url}> <NewsItem title={ element && element.title ? element.title.slice(0, 45) : "" } description={ element && element.description ? element.description.slice(0, 50) : "" } imageUrl={element.urlToImage} newsUrl={element.url} author={element.author} date={element.publishedAt} source={element.source.name} /> </div> ); })} </div> </div> </InfiniteScroll> </> ); }; export default News;
内容的提问来源于stack exchange,提问作者Himanshu Poddar
相关产品推荐
相关产品推荐

