I am working on converting a React component from a class component to a functional component with hooks.
I want to get a list of group members from my database. I hold the array in component state.
const [members, setMembers] = useState([]);
Once the members are downloaded, I want to get each member's profile picture asynchronously.
1) The component is mounted, and the following useEffect() is called. Note the dependency to getMembers.
useEffect(() => {
getMembers();
}, [getMembers]);
2) The useEffect callback calls the function getMembers(). Note the dependency to getMembersProfilePictures.
const getMembers = useCallback(() => {
fetchMembersFromDatabase()
.then((data) => {
setMembers(data);
getMembersProfilePictures();
})
}, [getMembersProfilePictures]);
3) Once the members are retrieved from the database, the members state is updated and getMembersProfilePictures() is called. Note the dependency to members.
const getMembersProfilePictures = useCallback(() => {
for (let i = 0; i < members.length; i++) {
const member = { ...members[i] };
if (member.has_picture) {
firebase
.storage()
.ref()
.child("<childUrl>")
.getDownloadURL()
.then((url) => {
member.picture_url = url;
const membersCopy = [...members];
membersCopy[i] = member;
setMembers(membersCopy);
});
}
}
}, [members]);
Because the useEffect() depends on getMembers(), getMembers() depends on getMembersProfilePictures(), and getMembersProfilePictures() depends on members, as soon as the members state is updated, the chain of useCallback()s is re-created, and the useEffect() is called. This becomes an infinite loop of data fetching.
My current thought is to pass the data retrieved from fetchMembersFromDatabase() directly to getMembersProfilePictures() as an argument. This removes the dependency of members from getMembersProfilePictures(), and therefore removes the infinite loop.
Ignoring listening to changes in the members list in the database and ignoring caching of members and their respective profile picture, it appears that there are no drawbacks to this solution. I am wondering what other's thoughts are to this solution. Thanks!
getMembersandgetMembersProfilePicturesare part of the same callback, you've separated them unnecessarily and created a dependency loop.getMembersProfilePictureswould no longer rely onmembersbecausememberscomes directly from thefetchMembersFromDatabasecall.OptionalThemeComponent = ({theme,children}) => theme ? <ThemeProvider theme={theme}>{children}</ThemeProvider> : childrenThis is what component based development should look like - components so small that you understand everything they do in under 1 minute.