简体   繁体   中英

Can I use two useEffect and have map inside a map

I am new to React and would like some help with the following problem. I current have this code.

 import React, { useState, useEffect } from "react";

function FetchData() {
  const [repos, setRepos] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch("https://api.github.com/orgs/org_name/repos")
      .then((res) => res.json())
      .then((data) => {
        setRepos(data);
      })
      .then(() => {
        setIsLoading(false);
      })
      .catch((err) => console.log(err));
  }, []);

return (
    <div>
      {repos.map((repo) => (
        <div key={repo.id}>
          <div>
            <h2>Name: {repo.name}</h2>
            <p>Top 5 Contributors</p>
))}

My above codes work fine, but my problem now is that I would like to add the top 5 contributors to the repository and to access that I have to go to https://api.github.com/repos/org_name/{repos}/contributors , and to get to that, I first have to use repo.contributor_url Should I use another useEffect and map to show the top 5 contributors?

Edit
Basically I want to do something like this.

useEffect(() => {
    fetch(`${repos.contributors_url}`)
      .then((res) => res.json())
      .then((data) => {
        setContributors(data);
        console.log(data);
      })
      .catch((err) => console.log(err));
  }, []);
...
<p> Top 5 Contributors: </p>
 <ul>
   {contributors.map((c, i) => {
   <li key={i}>{c.name}</li>
   )}
 </ul>

Since you are new to React. React used to have class based components to handle state and those class based components had special functions called- Life-Cycle-Methods . But from React 16.8 onwards React Community came up with React-Hooks and functional components can now be used to handle state and useState() and useEffect() are examples of Hooks.

Now useEffect() alone is used to do perform life-cycle method's work.

The way you have used useEffect() in your code is simulating componentDidMount() as you have kept the 2nd argument as an empty array []

We can use other life-cycle methods like componentDidUpdate() and comp.netnWillUnmount() using useEffect() Hook itself.

Then based on your requirement you can use useEffect() Hook as many times as required by your Component.

Coming to Updated part of your question now:

So, you basically need to do promise chaining . We know that fetch() is promise based,so when one asynchronous call is resolved and we get the first data, within your useEffect() hook only, you need to make another asynchronous request using the second url-end point to get the respective data.

Here is the updated code now: Try this

import React, { useState, useEffect } from 'react';

function FetchData() {
  const [repos, setRepos] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  const [contributors, setContributors] = useState([]);
  const [isContributorLoading, setIsContributorLoading] = useState(true);

  useEffect(() => {
    fetch('https://api.github.com/orgs/{org}/repos')
      .then((res) => res.json())
      .then((data) => {
        setRepos(data); // Data 1(repos) is received
        // Now We make another API call to get Data 2 (contributors)
        return fetch('https://api.github.com/repos/{org}/{repos}/contributors');
      })
      .then((res) => res.json()) // Chaining promise,handling 2nd Fetch request
      .then((data2) => {
        console.log(data2);
        setContributors(data2);
       })
      .then(() => {
        setIsLoading(false);
      })
      .catch((err) => console.log(err));
  }, []);


  return (
    <div>
      { repos.length && repos.map((repo) => (
        <div key={repo.id}>
          <div>
            <h2>Name: {repo.name}</h2>
          </div>
        </div>
      ))}

      <p> Top 5 Contributors: </p>
         <ul>
           {contributors.length && contributors.map((c, i) => {
             return <li key={i}>{c.name}</li>
           )}
          </ul>
    </div>
  );
}

So, basically you need to learn a bit more about how to use Hooks especially useEffect() , for now. Do some googling stuff, It would not be good if I tell you everything now. Give it a shot then.

You can directly call apis inside one useEffect.

 import React, { useState, useEffect } from "react"; function App() { const [repos, setRepos] = useState([]); const [contributor, setContributor] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function caller() { try { setIsLoading(true); const response = await fetch( "https://api.github.com/orgs/octokit/repos" ); const result = await response.json(); const contri = []; console.log(result); result.forEach((item) => { contri.push(fetch(`${item.contributors_url}`)); }); Promise.all(contri).then((contributorResults) => contributorResults).then((responses) => { console.log(responses); return Promise.all(responses.map((r) => r.json())); }).then((cont) => { setContributor([...cont]) }); setRepos(result); } catch (err) { console.log(err); } finally { setIsLoading(false); } } caller(); }, []); return ( <div> {repos.map((repo,index) => ( <div key={repo.id}> <h2> Name: {repo.name} </h2> { contributor[`${index}`] && contributor[`${index}`].slice(0,5).map(item => { return <div key={item.id}> <div>{item.login}</div> </div> })} </div> ))} {isLoading && <div>...loading</div>} </div> ); } export default App;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM