i have this function in useEffect which fetch posts from database and then set posts state then sending them to Posts component, i put the posts state in the useEffect dependency, so if a new post is created by user, the Posts component re-render so the user doesn't need to refresh the page and it worked fine !!
but i noticed at the network tab, requests to http://localhost:8000/home/${user._id} are being sent infinitely, i don't know why is that happening
const [posts, setPosts] = useState([]);
const { user, isLoading, error } = useContext(AuthContext);
useEffect(() => {
const getPosts = async () => {
const response = id
? await fetch(`http://localhost:8000/${id}`)
: await fetch(`http://localhost:8000/home/${user._id}`);
const data = await response.json();
setPosts(
data.sort((p1, p2) => {
return new Date(p2.createdAt) - new Date(p1.createdAt);
})
);
};
getPosts();
}, [user, id, user?._id, posts]);
return (
<div>
{posts.map((post) => (
<Posts key={post._id} post={post} />
))}
</div>
);
In the dependencies of your useEffect call, you've included posts. This is both unnecessary (posts isn't used inside the effect) and the cause of your troubles. Per the docs, everytime one of your dependencies changes, the effect is fired. Since getPosts modifies posts by calling setPosts, you have an infinite loop. The solution is to remove posts from the effect dependencies.
From your comments, it seems you also have a second question, which is, "how can I trigger a fetch operation when an external service (http://localhost:8000/) has an update ready for me?" There are a variety of answers to this question, but this simplest is to poll the external service on a timer. Something to the effect of the following should do:
useEffect(() => {
...
// fire getPosts once per second
const timer = setInterval(getPosts, 1000);
// clean up the timer whenever the effect is re-fired or the
// component unmounts
return () => clearInterval(timer);
}, [dependencies...]);
The cleanup step is critical. Without it, you will end up with multiple timers running at different intervals, and the timer(s) will not stop if the component is unmounted. Please see effects with cleanup in the docs for details.
This is not the way we generally handle this. Try this instead:
const [posts, setPosts] = useState([]);
useEffect with an empty array as dependency. This way you can avoid sending infinite requests. useEffect(() => {
const getPosts = async () => {
const response = id
? await fetch(`http://localhost:8000/${id}`)
: await fetch(`http://localhost:8000/home/${user._id}`);
const data = await response.json();
setPosts(
data.sort((p1, p2) => {
return new Date(p2.createdAt) - new Date(p1.createdAt);
})
);
};
getPosts();
}, []);
It infinitely update because the state posts is in your useEffect dependencies, so when you request the API, the state got updated and then when the state got updated, useEffect notice that and call getPosts() again and again
The way you handle this is by removing posts from the dependencies
If you want to update the page when the new post is created. You could add another state like [isUpdated, setIsUpdated] and put it in the dependencies instead of posts
and you can setIsUpdated(true) before requesting POST API and setIsUpdated(false) after
like this
function createNewPost() {
setIsUpdated(true)
...
<requesting POST API function>
...
setIsUpdated(false)
}