lorenzoajt
lorenzoajt

Reputation: 105

How to map img src with the results of an async function in React?

I'm trying to use the map function to render images with Material UI, but I have to fetch the url from the API before displaying them, that is what getFoto() is doing, but It displays nothing

return(
    <div className={classes.root}>
            
        <GridList cellHeight={180} className={classes.gridList}>
          <GridListTile key="Subheader" cols={2} style={{ height: 'auto' }}>
            
          </GridListTile>
          {data && data.map((tile) => (            

            <GridListTile key={tile.propertyId} >  
                                                         
            <Link to={`/AreasRegistradas/${tile.propertyId}`}>
              <img  src={(async () => {                         // <======Here is the problem
                  await getFoto(tile.propertyId)
              })()}
                    
                    alt={tile.propertyName} 
                    className={"MuiGridListTile-tile"}
              />
            </Link>
            
              
              <GridListTileBar
                title={tile.propertyName}
                subtitle={<span> {tile.address}</span>}
                actionIcon={
                  <div>
                    <IconButton aria-label={`info about ${tile.title}`} className={classes.icon} onClick={()=>console.log("edit")}>
                      <EditIcon />
                    </IconButton>                  
                    <IconButton aria-label={`info about ${tile.title}`} className={classes.icon} onClick={()=>console.log("delete")}>
                      <DeleteForeverIcon />
                    </IconButton>                  
                  </div>
                }
              />
            </GridListTile>
          ))
        }
        </GridList>

        

    </div>
  )

However, if I do console.log (await getFoto(tile.propertyId)) it returns the correct urls that I need

//.....
<img  src={(async () => {
                console.log(await getFoto(tile.propertyId))  //this returns the values that I need in the console            
          })()}
//.....

What can be the problem here? I'm new in this async functions world please help. Thanks!

Im using:

-"react": "^16.13.1"

Upvotes: 0

Views: 4282

Answers (2)

lorenzoajt
lorenzoajt

Reputation: 105

Thanks to see sharper for the advice!!! Here's what I did: First I created a new component called < Tile /> , added it inside my original map function and passed the item as props:

//...
          {data && data.map((tile) => (            
              <div key={tile.propertyId}> 
                <Tile tile={tile} />
              </div>
            ))
          }
//...

Then inside my new < Tile /> component I added what I had originally inside my map function plus the async function inside a useEffect hook and store the fetched url in a useState hook:

function Tile(props){
  const {tile} = props  
  const [imgSrc, setImgSrc] = useState(''); // here is the hook for the url
   
  useEffect(() => {
    const getFoto = async (propId) =>{          
      try{
        const url = `....url/${propId}/images`
        const response = await fetch(url, {
          //authorization stuff
          }
        });

        const responseData = await response.json()       
        setImgSrc(responseData.items[0].imageUrl)  //setting the fetched url in a hook
      }catch(error){
      console.log(error)
      } 
      }

    getFoto(tile.propertyId);
  }, []);


    const useStyles = makeStyles((theme) => ({
      root: {
        display: 'flex',
        flexWrap: 'wrap',
        justifyContent: 'space-around',
        overflow: 'hidden',
        backgroundColor: theme.palette.background.paper,
      },
      gridList: {
        width: 500,
        height: 450,
      },
      icon: {
        color: 'rgba(255, 255, 255, 0.54)',
      },
    }));
    const classes = useStyles();
   

  return(
    <GridListTile className={"MuiGridListTile-tile"}>  
                                               
      <Link to={`/AreasRegistradas/${tile.propertyId}`}>

        <img  src={imgSrc}                    
              alt={tile.propertyName} 
              className={"MuiGridListTile-tile"}
        />
      </Link>

        
        <GridListTileBar
          title={tile.propertyName}
          subtitle={<span> {tile.address}</span>}
          actionIcon={
            <div>
              <IconButton aria-label={`info about ${tile.title}`} className={classes.icon} onClick={()=>console.log("edit")}>
                <EditIcon />
              </IconButton>                  
              <IconButton aria-label={`info about ${tile.title}`} className={classes.icon} onClick={()=>console.log("delete")}>
                <DeleteForeverIcon />
              </IconButton>                  
            </div>
          }
        />
    </GridListTile>
  )

}

Thanks again!

Upvotes: 0

see sharper
see sharper

Reputation: 12055

When you set src={await getFoto(...)} you are setting the src attribute (a string obviously) to a Promise, which clearly won't work. Rather, somewhere in your component code, such as the componentDidMount event, you should fetch the image and set the result to some state variable which then becomes the src:

async componentDidMount() {
  const photo = await getFoto(tile.propertyId);
  this.setState({photo});
}
...
render() {
  ...
  <img src={state.photo} />

But note, this is assuming that what is returned is photo URL. If it's the image itself, you'll need to use base64. Something like src={data:image/png;base64,${state.photo}}. It also assumes title is in scope in the componentDidMount method. If it isn't, you'll need to use the correct reference (e.g. this.tile, this.props.tile?).

Upvotes: 1

Related Questions