I'm relatively new to React, but I have been using useEffect to get all of my news and events from the database on load of a page. I recently tried to implement a loading spinner, and now I seem to be hitting an infinite loop, but I cannot understand why. I have the news and events in the dependency array, because if they change, then we need to update what is displayed to the user. The only potential issue I can see is that I am building up the news and events arrays in my action in my state store, but I haven't seemed to encounter this issue until trying to implement this functionality. Its probably something stupid, but can anyone help me?
Front end code:
export default observer(function NewsAndEvents() {
const { generalStore } = useStore();
const { getNewsAndEvents, events, news, loading } = generalStore
useEffect(() => {
getNewsAndEvents();
}, [news, events]);
return (
<main id="newsAndEvents">
<div className="infoContainer">
<h1>News</h1>
{ loading ? <LoadingSpinner /> : news.map((news, index) => {
return <News key={`news_${index}`} title={news.title} content={news.content} />
})}
</div>
Get action:
export default class GeneralStore {
isAuthenticated: boolean = false;
selectedEvent: Event | undefined = undefined;
events: Event[] = [];
selectedNews: News | undefined = undefined;
news: News[] = [];
loading: boolean = false;
constructor() {
makeAutoObservable(this);
}
getNewsAndEvents = async () => {
this.loading = true;
try {
let response: any = await agent.newsAndEvents.list();
if (response !== null) {
runInAction(() => {
this.events = [];
this.news = [];
response.events.forEach((item: any) => {
let newEvent: Event = {
id: item._id,
date_time: item.date_time,
name: item.name,
description: item.description,
price: item.price,
type: ContentType.event,
};
this.events.push(newEvent);
});
response.news.forEach((item: any) => {
let newNews: News = {
id: item._id,
title: item.title,
content: item.content,
type: ContentType.news,
};
this.news.push(newNews);
});
this.setLoading(false);
});
}
} catch (ex) {
console.log(ex);
this.setLoading(false);
}
};
}
I am using TS and MobX.
CodePudding user response:
if your getNewsAndEvents()
function is updating news
or events
, your useEffect will run infinitely. You need to create a flag to prevent this.
const [flag, setFlag] = useState(true);
useEffect(() => {
if (flag) {
getNewsAndEvents();
setFlag(false);
}
}, [flag]);
Ideally flag should be a global state too but I used useState here cuz I'm not familiar with mobx.
Then every time you want getNewsAndEvents()
to run, you can set the flag to true.
CodePudding user response:
I think the problem is when the loading ends, its value (true) stays the same because loading is not a component state, so the component will not be rerendered if the loading ends, so to solve that, I suggest you put the loading value in a state using useState hook like that:
const [loading, setLoading] = useState(generalStore.loading);
then you will add a useEffect with a [generalStore.loading] dependency so it will update the state after the loading value changes, and that will make the component rerender:
useEffect(() => {
setLoading(generalStore.loading);
}, [generalStore.loading]);
I'm not sure this solution will work for you, but I think it deserves a try.