I have this code, where I am using useEffect to update my page with the infinite scroll. However while compiling the same, I see this error
src\components\News.js
Line 40:6: React Hook useEffect has missing dependencies: 'props.category' and 'updateNews'. Either include them or remove the dependency array react-hooks/exhaustive-deps
But when I am including this function in the dependency array [page, props.category, updateNews]
I am getting another error
Line 17:9: The 'updateNews' function makes the dependencies of useEffect Hook (at line 40) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'updateNews' in its own useCallback() Hook react-hooks/exhaustive-deps
Here is my code : Whenever user scrolls the page number is updated and the use Effect hook is being called, which in turn is calling the updateNews function.
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);
};
const updateNews = async () => {
props.setProgress(10);
let goToPage = page;
const url = `https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${goToPage}&pageSize=${props.pageSize}`;
props.setProgress(30);
let data = await fetch(url);
props.setProgress(50);
let parsedData = await data.json();
props.setProgress(70);
if (parsedData) {
setArticles(articles.concat(parsedData.articles));
setLoading(false);
setPage(page);
setTotalResults(parsedData.totalResults);
}
props.setProgress(100);
};
useEffect(() => {
updateNews();
// eslint-disable-next-line
document.title = `${capitalizeFirstLetter(props.category)} - NewsMonkey`;
}, [page, props.category]);
const fetchMoreData = async () => {
setPage(page + 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;
Note : I tried the answer that I was getting as suggestion to this question but that did not work for me.
Your solution is actually provided inside the error. After including updateNews
inside the dependency array, without any memorization on every rerender React will always see updateNews !== updateNews
, which is due to the nature of JS (even equally written functions will never be equal to one another).
Wrapping the updateNews
function inside useCallback
basically allows React to know that this function will be the same on every rerender until one of its dependency array items change.
This should work:
const updateNews = useCallback(async () => {
props.setProgress(10);
let goToPage = page;
const url = `https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${goToPage}&pageSize=${props.pageSize}`;
props.setProgress(30);
let data = await fetch(url);
props.setProgress(50);
let parsedData = await data.json();
props.setProgress(70);
if (parsedData) {
setArticles(articles.concat(parsedData.articles));
setLoading(false);
setPage(page);
setTotalResults(parsedData.totalResults);
}
props.setProgress(100);
}, [page, props.country, props.category, props.apiKey, props.pageSize]);
useEffect(() => {
updateNews();
// eslint-disable-next-line
document.title = `${capitalizeFirstLetter(props.category)} - NewsMonkey`;
}, [props.category, updateNews]);