If you pass undefined as the value of the array, it's the same as passing no value at all, i.e. the code will run on every render.
If, OTOH you pass [undefined] as the value of the array, it's the same as passing an empty array ([]) or an array of values that never change. I.e. the code will run only on the initial render.
What happens with useEffect having either null or undefined as a dependency?
Using a property of initially undefined variable for useEffect dependency
useEffect should return `undefined`
Why does eslint warn about useEffect having a missing dependency?
I was watching a tutorial video on useEffect, and i understand how useEffect fires relative to change in dependency, but what if there's an explicit "null" or "undefined" given as a dependency?
I mean.
useEffect(()=>{
},[Null/undefined])
Anybody?
So I call two functions in useEffect - getIds() and getNews() which set the results to states newsIds and latestNews. Without newsIds I can't get latestNews so I first need to get ids.
The thing is, with this code I get undefined on each console.log twice. If I remove empy array from the useEffect dependencies, I get what I need because it sends infinite api calls and on third api request and the following I get the results. But of course I don't want to have infinite requests running.
So what's causing it? did I do something wrong in useEffect or in functions?
Thank you so much.
export function NewsCardList() {
const [newsIds, setNewsIds] = useState<number[]>();
const [latestNews, setLatestNews] = useState<NewsItem[]>();
const getIds = async () => {
await fetch(https://hacker-news.firebaseio.com/v0/newstories.json)
.then((res) => res.json())
.then((data: number[]) => data.filter((id: number, i: number) => i < 100))
.then((data) => setNewsIds(data))
.catch((e) => console.log(e));
};
const getNews = async () => {
let urls = newsIds && newsIds?.map((id) =>
https://hackernews.firebaseio.com/v0/item/${id}.json);
let requests = urls && urls.map((url) => fetch(url));
console.log(urls);
console.log(requests);
requests && await Promise.all(requests)
.then((responses) => Promise.all(responses.map((r) => r.json()))
.then((news) => setLatestNews(news)));
};
useEffect(() => {
getIds();
getNews();
console.log(newsIds);
console.log(latestNews);
}, []);
return (
<>
{latestNews && latestNews.map((news) => (
<NewsCard key={news.id}
author={news.by}
title={news.title}
date={news.time}
rating={news.score} /> ))}
</> ); }I would recommend writing this as follows:
const previousFooRef = useRef(props.foo);
useEffect(() => {
if (previousFooRef.current !== props.foo) {
animateSomething(ref, props.onAnimationComplete);
previousFooRef.current = props.foo;
}
}, [props.foo, props.onAnimationComplete]);
You can't avoid the complexity of having a condition inside the effect, because without it you will run your animation on mount rather than just when props.foo changes. The condition also allows you to avoid animating when things other than props.foo change.
By including props.onAnimationComplete in the dependencies array, you avoid disabling the lint rule which helps ensure that you don’t introduce future bugs related to missing dependencies.
Here's a working example:
Suppress the linter because it gives you a bad advice. React requires you to pass to the second argument the values which (and only which) changes must trigger an effect fire.
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps
It leads to the same result as the Ryan's solution.
I see no problems with violating this linter rule. In contrast to useCallback and useMemo, it won't lead to errors in common case. The content of the second argument is a high level logic.
You may even want to call an effect when an extraneous value changes:
useEffect(() => {
alert(`Hi ${props.name}, your score is changed`);
}, [props.score]);