In this post, I'll share how I ended up calling a Hook multiple times inside a component.
I will take the two approaches I followed (the first one is wrong but intuitive, the second one is correct but not evident).
Let's suppose that we create a Hook that counts the number of posts of a user. Let's call this Hook usePosts.js
:
jsximport React, {useState, useEffect} from 'react'; import getNumberOfPosts from '../Functions/getNumberOfPosts'; const usePosts = (uid) => { const [posts, setPosts] = useState(0); useEffect( async () => { // Returns the number of posts for a user with uid let total = await getNumberOfPosts(uid); // Setting the total of posts setPosts(total); }, []); return posts; } export default usePosts;
Days later, let's suppose that we create Ranking.js
to know the number of posts for each user. The component does as follows:
jsximport React, {useState, useEffect} from 'react'; import getUsers from '../Functions/getUsers'; import usePosts from '../Hooks/usePosts'; const Ranking = () => { const [users, setUsers] = useState([]); useEffect( async () => { // Returns a user id array let array = await getUsers(); // Setting the array of users setUsers(array); }, []); // 🔴 At this point we want to know the posts of each user // 🔴 So, first choice would be use map function, but... // 🔴 This solution is wrong // 🔴 Because you can't call a Hook inside a loop or a function const usersPosts = users.map( uid => usePosts(uid) ); } export default Ranking;
Instead of one argument, useHooks.js
will get several. We can use the spread operator ...
to accomplish this. So, let's modify the Hook:
jsximport React, {useState, useEffect} from 'react'; import getNumberOfPosts from '../Functions/getNumberOfPosts'; const usePosts = async (...uids) => { const [posts, setPosts] = useState([]); useEffect( () => { // Looping through uids parameter let total = await Promises.all( uids.map( uid => getNumberOfPosts(uid)) ); // Setting the total of posts setPosts(total); }, []); return posts; } export default usePosts;
Now the Hook returns an array of posts depending on the uids
parameter.
If we want to rank the users:
jsximport React, {useState, useEffect} from 'react'; import getUsers from '../Functions/getUsers'; import usePosts from '../Hooks/usePosts'; const Ranking = () => { const [uids, setUids] = useState([]); useEffect( async () => { // Listener that returns a user id array let array = await getUsers(); // Setting the array of users setUids(array); }, []); // ✅ At this point, we want to know the posts for each user // ✅ Now we can pass an array of users as parameter // ✅ And we will get an array in return const usersPosts = usePosts(...uids); return usersPosts; } export default Ranking;
Rather than calling the Hook multiple times, it was better to pass an array of arguments to the Hook and to get an array of results.
Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.