Home > Software engineering >  How to pass a function to each mapped items from an array?
How to pass a function to each mapped items from an array?

Time:09-24

On a component, i mapped each item from an array that comes from an API, and am returning them on a <li> each, like this:

const usersComponent = usersData.map((user: any) => (
      <li
        key={user.Username}
        className="px-4 my-2 lg:pl-8 w-full scale-95 transform transition duration-500 hover:scale-100"
      >
      \\ rest of the code

Then, on the component, i do:

return (
  <ul>{usersComponent}</ul>
)

Now, each item on this list is a username in my API, and these usernames have groups on my database, but the groups only return in another GET from the API, which i have on this other function:

async function listGroupsForUser(username: string) {
    const apiName = 'AdminQueries'
    const path = '/listGroupsForUser'
    const myInit = {
      body: {
        username
      }, 
      headers: {
        'Content-Type': 'application/json',
        Authorization: `${(await Auth.currentSession())
          .getAccessToken()
          .getJwtToken()}`,
      },
    }
    return await API.get(apiName, path, myInit)
  }

So, i need to pass this listGroupsForUser for each of the <li> i mapped, then it will return an array for each one of them, and i need to map it, for each one, individually.

I tried in many ways but couldn`t do it.

CodePudding user response:

Depends if you need to fetch all the data upfront, or if you can fetch the groups asynchronously.

To fetch them all ahead, you could use Promise.All and fire off the queries in parallel like:

const getRandomDelay = (min: number, max: number): number => {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min   1)   min);
};

// mock API call
const fetchGroups = (username: string): Promise<string[]> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      return resolve(['admin', 'editor']);
    }, getRandomIntInclusive(2000, 4000));
  });
};

const data = ['darth', 'fett', 'luke'];

interface User {
  name: string;
  groups?: string[];
}

export default function App() {
  const [users, setUsers] = useState<User[] | null>(null);

  useEffect(() => {
    Promise.all(
      data.map(async (user) => {
        const groups = await fetchGroups(user);
        return {
          name: user,
          groups: groups
        };
      })
    ).then((result) => {
      setUsers(result);
    });
  }, []);

  return (
    <div className="App">
      {users &&
        users.map((user) => (
          <div key={user.name}>
            {user.name}&nbsp;
            {user.groups.map((group) => (
              <span key={group}>{group}, </span>
            ))}
          </div>
        ))}
    </div>
  );
}

Alternately, you could use a hook within each. Something like:

const useListGroupsForUser = (username: string) => {
  const [groups, setGroups] = useState<string[] | null>(null);

  useEffect(() => {
    console.info(`mocking fetch for ${username}`);
    fetchGroups(username).then((result) => setGroups(result));
  }, [username]);

  return {
    groups
  };
};

// User component
const User = ({ user }: UserProps) => {
  const { groups } = useListGroupsForUser(user);

  return (
    <li>
      {user} <br />
      {groups ? groups.map((group) => group) : <span>loading groups...</span>}
    </li>
  );
};

// and render in App
{data.map((user) => (
    <User key={user} user={user}/>
))}
  • Related