-1

I am building a component tat get the members of a group and then download the image from the api. It uses async await in the componentDidMount and the photos come back correctly. however even though I am using async await. it doesn't wait for the images to resolve. Thus in setState it will return a promise.

my component:

import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faUser } from "@fortawesome/free-solid-svg-icons";
import {
    GetGroupMembers,
    getImage
} from "../../../HttpRepositories/oneApiRepository";

class TeamCardPersonaMembers extends React.Component {
    constructor(props) {
        super(props);
        this._isMounted = false;
    }

state = {
    members: []
};

async componentDidMount() {
    this._isMounted = true;
    let members = await GetGroupMembers(this.props.teamId);

    const membersToSend = await members.map(async member => {
        const memberPhotoUrl = `${process.env.REACT_APP_ONE_API_URL}/api/users/${
            member.id
        }/photo`;

        let memberPhoto = await getImage(memberPhotoUrl);
        member.photo = memberPhoto;
        return member;
    });

    if (this.state.member !== "error" && this._isMounted) {
        this.setState({ members: membersToSend });
    }
}

render() {
    let members = [];

    console.log(this.state);

    if (this.state.members.length > 0) {
        this.state.members.map(member => {
            console.log("this is the member ", member);
            const memberImg = (
                <img
                    key={member.id}
                    src={member.photo}
                    alt={member.displayName}
                    className="team-card-persona-wrapper"
                    title={member.displayName}
                />
            );

            members.push(memberImg);
        });
    }

    return <React.Fragment>{members}</React.Fragment>;
}

componentWillUnmount() {
    this._isMounted = false;
}

}

console.log

enter image description here

export default TeamCardPersonaMembers;

any help or pointers will be greatly appreciated! Cheers!

Rodney Wormsbecher
  • 897
  • 2
  • 17
  • 39

2 Answers2

1

The standard implementation of map does not await the callback passed to it. Resulting in the map call returning promises before the callbacks complete. You have two choices.

    const membersToSend = []
    for (const member of members) {
        const memberPhotoUrl = `${process.env.REACT_APP_ONE_API_URL}/api/users/${
            member.id
        }/photo`;

        let memberPhoto = await getImage(memberPhotoUrl);
        member.photo = memberPhoto;
        membersToSend.push(member);
    }

or to roll your own implementation of async map.

Array.prototype.mapAsync = async function (callback) {
  const output = []
  for (const el of this)
    output.push( await callback(el) ) <-- // Note the await on the callback

  return output
}

Edit: Note that the above method executes the promises in sequence. As Jed points out, if you want to fire all the requests at once and wait for them to resolve,

const memberPhotos = await Promise.all(members.map(member => {
   const memberPhotoUrl = `${process.env.REACT_APP_ONE_API_URL}/api/users/${
            member.id
        }/photo`;

        return getImage(memberPhotoUrl);
}))

But this method could overload your network if there are too many requests, use with caution.

Avin Kavish
  • 8,317
  • 1
  • 21
  • 36
  • I choose the for loop. thanks for the help. guess I should have checked MDN for the async support. Cheers. will accept as answer when it's possible – Rodney Wormsbecher Jun 06 '19 at 09:08
0

The await keyword only works on other async functions, or functions that return a Promise. So I don't think it makes sense to await on members.map like you are. That is just a normal sync function call. You need to set it up more like:

https://flaviocopes.com/javascript-async-await-array-map/

Jed Richards
  • 12,244
  • 3
  • 24
  • 35