0

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"

2
  • You can do it in alternate method like: getFoto().then(src=> <img src={src}/>) Commented Jul 8, 2020 at 0:19
  • No, please don't run async functions inside render. Commented Jul 8, 2020 at 0:28

2 Answers 2

1

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?).

Sign up to request clarification or add additional context in comments.

2 Comments

Great idea! But I need the propertyId to call the getFoto() function, and I access that inside my map function ( tile => tile.propertyid). If i call the getFoto inside the componentDidMount at the begginning how can I set this properyId for each property inside my map function?
I see. In that case, each tile should be its own component. You can use a function component with useEffect and useState (see eg robinwieruch.de/react-hooks-fetch-data), or a class component with its own componentDidMount and setState.
0

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.