bourne2077
bourne2077

Reputation: 171

Using React useEffect in a loop to fetch data from an API multiple times

Hi I'm new to Node/React, and I'm creating a learning project. It's platform that connects freelancers with nonprofit companies. Users (freelancers), view a list of companies, and click a button to connect to a company. Once this is clicked, the user will have that company added as a relationship in the database. This is working correctly.

Now I'd like users to visit a page and view their "connections", basically all the companies they connected with. In the database, I'm only storing the "companyHandle" in the association table. So currentUser.connections would give me an array of companyHandles ['aapl', 'gogl', 'msft']. So when the user views their connections, I would like to fetch data from the backend API, to retrieve more information about each company (companyName, numEmployees, etc..).

I tried to do that by looping through all the companyHandles, and fetching data from the API for each company, but I get the error React Hook "useEffect" may be executed more than once. Possibly because it is called in a loop...

What's a better way to do this? How can I fetch data for different companies? Below is my code (shortened)

schema

CREATE TABLE companies (
  company_handle VARCHAR(25) PRIMARY KEY,
  password TEXT NOT NULL,
  company_name TEXT NOT NULL,
  country TEXT NOT NULL,
  num_employees INTEGER CHECK (num_employees > 0)
);

CREATE TABLE users (
  username VARCHAR(25) PRIMARY KEY,
  password TEXT NOT NULL,
  first_name TEXT NOT NULL,
  last_name TEXT NOT NULL,
  email TEXT NOT NULL CHECK (position('@' IN email) > 1)
);

CREATE TABLE connections (
  username VARCHAR(25)
    REFERENCES users ON DELETE CASCADE,
  company_handle VARCHAR(25)
    REFERENCES companies ON DELETE CASCADE,
  PRIMARY KEY (username, company_handle)
);

UserConnections.js

function UserConnections() {
  const { currentUser } = useContext(UserContext);
  const connections = currentUser.connections;
  const [companies, setCompany] = useState([]);

  if (connections) {
    for (const connection in connections) {
      useEffect(function getCompanyDetail() {
        async function getCompany() {
          setCompany(await VolunteerApi.getCurrentCompany(connection));
        }
        getCompany();
      }, [connection]);
    }
  }

  return (
    <div>
      {companies.length
        ? (
          <div>
            {companies.map(company => (
              <CompanyCard
                key={company.companyHandle}
                companyHandle={company.companyHandle}
                companyName={company.companyName}
                country={company.country}
                numEmployees={company.numEmployees}
              />
            )
            )}

          </div>
        ) : (
          <p>You have no connections</p>
        )}
    </div>
  );
};

Upvotes: 1

Views: 449

Answers (2)

Jaynish Buddhdev
Jaynish Buddhdev

Reputation: 21

You can use promise.all to execute promises (api fetcher) and useEffect hook from React.

useEffect(() => {
   setLoading(true); // If you want to add loader or spinner till companies info fetched
   const connectionsPromises = connections.map((connection) => {
     return VolunteerApi.getCurrentCompany(connection);
   });

   Promise.all(connectionsPromises).then((companies) => {
      setCompany(companies); // save companies 
      setLoading(false); // disable loader 
   });
}, [connections])

Promise.all resolves promises parallelly

Upvotes: 2

Marlom
Marlom

Reputation: 688

Basically, you can only use hooks at the top level of your component.

Take a look at the rules of hooks (including useEffect).

You should try to put your if and for loop inside the useEffect.

Upvotes: 0

Related Questions