React useEffect non-stop fetching - reactjs

I'm trying to fetch some data from the backend and display those data in a dropdown menu.
const[allGroups, setAllGroups] = useState([])
useEffect(() => {
console.log("useEffect")
// get all Groups
fetch("http://localhost:8080/group/getAll")
.then(response => response.json())
.then(data => {
let arr = []
for (let i = 0; i < data.length; i++){
arr.push(data[i]["groupName"])
}
setAllGroups(arr)
})
}, [allGroups])
And this is where I generate things
<Dropdown.Menu>
<Dropdown.Item href="#/action-1">Action</Dropdown.Item>
{
allGroups.map((group, i) => {
return (
<Dropdown.Item key={i}>{group}</Dropdown.Item>
)
})
}
</Dropdown.Menu>
I noticed the useEffect hook was running basically all the time (even if the value of allGroups didn't change). I thought useEffect only runs when the specified elements on the page rerender. Is there a way to only trigger useEffect when allGroups change? Thanks!

Is there a way to only trigger useEffect when allGroups change?
That's exactly what your code is doing already. However, allGroups is changing each time the function is run (since you're calling setAllGroups as part of that function).
If you only want the items to be fetched once (after component's first render), remove allGroups from the deps array.
If you want to re-fetch the data in response to some user action, define a separate method that can be explicitly called, and then call it appropriately (e.g. when a button is clicked) in addition to calling it within useEffect with an empty deps array.

Just remove allGroups params from useEffect dependencys
this happened because on each render you fetch your data and when fetch success you set new data to all Groups state that make useEffect rerender
const[allGroups, setAllGroups] = useState([])
useEffect(() => {
getAllGroups()
}, [])
const getAllGroups = () => {
fetch("http://localhost:8080/group/getAll")
.then(response => response.json())
.then(data => {
let arr = []
for (let i = 0; i < data.length; i++){
arr.push(data[i]["groupName"])
}
setAllGroups(arr)
})
}

by using allGroups inside the useEffect the useEffect will be re-render the component as long the allGroups change.
you just need to fetch the APIS in the first render for the component that will lead you to just use useEffect with empty dependency like this []. after getting the response just update the state by using setAllGroups. then implement what you need on allGroups
const[allGroups, setAllGroups] = useState([])
useEffect(() => {
console.log("useEffect")
// get all Groups
fetch("http://localhost:8080/group/getAll")
.then(response => response.json())
.then(data => {
setAllGroups(data) <= just update the state with the response here.
})
}, []) <= you don't need to add **allGroups** here

Related

Results from realtime db not available when called

Im having some trouble with realtime database and react native.
I have the following useEffect in a component that is supposed to listen for changes and then update state as required, I then use that state to populate a list.
The component gets a data object passed as a prop, the data object contains a string array called members that contains uuids, I am trying to iterate over those to get the attached user from realtime db and then save those objects to a state array.
const myComponent = ({ data }) => {
const [users, setUsers] = useState([]);
useEffect(() => {
const userArr = [];
data.map(item => {
item.members.forEach((username: string) => {
database()
.ref(`users/${username}`)
.on('value', snapshot => {
userArr.push(snapshot.val());
});
});
});
setUsers(userArr);
};
}, []);
return (
<>
{users} <----- this is in a flatlist
</>
);
}
It works eventually after refreshing the screen about 5 times. Any help would be greatly appreciated.
The simplest way to get some data to show is to move update the state right after you add an item to the array:
useEffect(() => {
const userArr = [];
data.map(item => {
item.members.forEach((username: string) => {
database()
.ref(`users/${username}`)
.on('value', snapshot => {
userArr.push(snapshot.val());
setUsers(userArr); // 👈
});
});
});
};
}, []);
Now the UI will update each time that a user is loaded from the database.
I also recommend reading some more about asynchronous loading, such as in Why Does Firebase Lose Reference outside the once() Function?
Your database call may be asynchronous, which is causing the code inside the useEffect to act a little funny. You could push all those database calls (while iterating through item.members) into an array, and then do Promise.all over the array. Once the promises are resolved, you can then set the users.
Hope this helps!
add an async function inside useEffect and call it
useEffect(() => {
const getUsers = async () => {
const userArr = [];
data.....
//wait for it with a promise
Promise.all(userArr).then(array => setUsers(array))
})
getUsers()
}, [])
not sure if the function needs to be async

How to call api inside of a loop and perform action on it inside of useEffect React Native

Here is my scenario:
I'm having a cart object in Redux store having information in the form of array of objects having sellerId and the array of products, and I want to map on each object to get sellerId and then fetch seller's data from API on page load.
Here's my code
const [uniqueSellers, setUniqueSellers] = useState([]);
useEffect(() => {
const uniqueSellerIds = [];
cart.filter((item) => {
if (!uniqueSellerIds.includes(item.sellerId)) {
uniqueSellerIds.push(item.sellerId);
}
});
if (uniqueSellerIds.length === 1) setItems(["Seller's delivery"]);
uniqueSellerIds.map((sellerId) =>
axios.get(`${devBaseURL}/sellers/${sellerId}`).then((res) => {
setUniqueSellers((prev) => [
...prev,
{
sellerId: res.data.data[0]._id,
sellerProvince: res.data.data[0].businessAddress.province,
},
]);
}),
);
// Here I want to perform some operations on uniqueSellers state, but it's not available here
console.log('uniqueSellers: ', uniqueSellers); // logs empty array
setLoading(false);
return () => {
setUniqueSellers([]);
};
}, []);
Mutating state is an async process. Fetch operations are also async. So, your console log always executes before your axios call and setUniqueSellers hook.
Listen changes in uniqueSellers array inside another useEffect by giving it as a dependency.
useEffect(() => {
console.log(uniqueSellers); //will log after every change in uniqueSellers
}, [uniqueSellers])

Updated array needs to be set with react hooks but creates infinite loop

in my code below I am fetching images from Firebase Firestore. The url is returned properly and is pushed to the images state
Now the problem is that identical urls are being added (because the useEffect runs to soon or early?)
So in the second useEffect i update the images array and remove duplicates nice and easy with ...new Set, but the problem is that setImages() is also called there which is not allowed since this creates the infinite loop.
So my question is, can someone tell me where to set the updated array uniq to the images state the right way?
Thanks in advance!
const [evtsFiltered, setEvtsFiltered] = useState([])
const [images, setImages] = useState([])
useEffect(() => {
evtsFiltered?.map((item) => {
storageRef
.child(item)
.getDownloadURL()
.then((url) => {
setImages((images) => [...images, url]) // url returns a plain string such as 'http://image-1.png'
})
})
}, [evtsFiltered])
useEffect(() => {
let uniq = [...new Set(images)] // Duplicates are removed
setImages(uniq) // Infinite loop problem
}, [images])
Improvements needed in your code:
You don't need to use optional-chaining with evtsFiltered because its initial value is an empty array.
map() is not the right method to use if you are just going to iterate over the array. You could use map() method along with Promise.all() to fetch image URLs.
Identical URLs could not be because of useEffect hook. Either you have duplicate URLs in the firebase storage or you are not updating the state correctly.
You wouldn't need the second useEffect hook if you update the images state correctly in the first useEffect hook.
Try updating the state using Promise.all() and map() method as shown below:
useEffect(() => {
const arr = evtsFiltered.map((item) => {
return storageRef.child(item).getDownloadURL();
});
Promise.all(arr)
.then(urls => {
setImages(urls);
})
.catch(err => /* handle error */ );
}, [evtsFiltered]);
Using map() method, create an array of promises that you can then resolve using Promise.all() method. Also, remove the 2nd useEffect hook.
It's because you try to update state on which you listen tov updated. One solution is to create another state which contains unique images.
const [evtsFiltered, setEvtsFiltered] = useState([])
const [images, setImages] = useState([])
const [uniqueImages, setUniqueImages] = useState([])
useEffect(() => {
evtsFiltered?.map((item) => {
storageRef
.child(item)
.getDownloadURL()
.then((url) => {
setImages((images) => [...images, url]) // url returns a plain string such as 'http://image-1.png'
})
})
}, [evtsFiltered])
useEffect(() => {
let uniq = [...new Set(images)] // Duplicates are removed
setUniqueImages(uniq) // Infinite loop problem
}, [images])
Another to set unique images just in first effect.
.then((url) => {
setImages((images) => [ ...new Set([ ...images, url])]) // url returns a plain string such as 'http://image-1.png'
})

React useEffect causing infinite re-render despite passing argument to dependency array

The 'myPosts' has an object with multiple posts inside it.. I wanted the user profile to immediately show the post after it is uploaded so I passed 'myposts' in the dependency array.
But the problem is that the component is re-rendering infinitely. How can I make it so that it re-renders once, only when a new post is uploaded? I can't understand why passing 'myposts' in the array is causing infinite renders instead of only once.
const [myposts, setPosts] = useState([]);
useEffect(() => {
fetch('/mypost', {
headers: {
cookie: 'access_key',
},
})
.then((res) => res.json())
.then((data) => {
// console.log(data);
setPosts(data.myposts);
});
}, [myposts]);
When fetch resolves, it modifies myposts, which triggers a fetch because it is listed as dependency of useEffect, which modifies myposts, and so it continues...
It seems that myposts depends on the result of the fetch, not the other way around. So I would suggest removing myposts from the dependency list.
The useEffect hook is called when myposts gets updated. In the final .then of your fetch, you're updating it via setPosts. The best way to fix this is by making the dependency array an empty array.
But this won't solve the issue of updating posts from the server, but this can also be done in a periodic function with setInterval. This would result in something like the code below.
const [myposts, setPosts] = useState([]);
const update = fetch('/mypost', {
headers: {
cookie: 'access_key',
},
})
.then((res) => res.json())
.then((data) => {
// console.log(data);
setPosts(data.myposts);
});
useEffect(() => {
update()
const interval = setInterval(update, 30000)
return () => clearInterval(interval)
}, []);

After Pushing a Array Element in react.js using useState and useEffect it's looping infinite after update the state. How to solve?

I have push some a image elements in object array it's working fine but how to update the state?
const url = "https://jsonplaceholder.typicode.com/posts";
const [posts, setPosts] = useState([]);
const postsMap = posts.map(post => {
return {...post, "image": `image-${post.id}.jpg`}
})
console.log("Added Post Image", postsMap);
useEffect(()=>{
fetch(url)
.then(res => res.json())
.then(data => {
setPosts(data)
console.log(data);
})
}, [postsMap]);
I would say its because -
setPosts(data) in useEffect() updates post
update in post updates postsMap
update in postsMap triggers a re-render of the component
repeat the cycle.
I think the infinite loop is probably because of the wrong parameter passed to the dependency array provided to useEffect as the second parameter. I think passing an empty array as a parameter to useEffect's second argument should solve the problem.
Because your useEffect is dependend on a variable that changes within it self (setPosts). So you create an infinite Loop. You don’t need postmap as dependency, use url as dependency.

Resources