madLad
madLad

Reputation: 155

ReactJS: Rendering an array of images

I'm still new to ReactJS and I'm currently fiddling around with Spotify API and it works great but I stumbled into an issue whilst trying to render an array of images that were retrieved via the API. At first, I tried calling it inside the render() component but it only produces a list of empty 'undefined' images with only the alt attribute showing up. Secondly, I tried it inside return()and just has the same symptom as the former, this time without any of the images at all.

This issue only occurs however when I try using the .map() method; when I render an image individually, it works just fine.

import React, { Component } from "react";

let someData = "hello";

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      playlists: { name: [], plist_id: [], albumArt: [] }
    };
  }

  getPlaylists(someData) {
    let plistNames = [];
    let plistContents = [];
    let plistArts = [];

    someApi
      .searchPlaylists(someData)
      .then(data => {
        if (data.playlists.items) {
          if (data.playlists.items.length > 0) {
            console.log(data);
            data.playlists.items.forEach(plist => {
              plistNames.push(plist.name);
              plistContents.push(plist.id);
              plistArts.push(plist.images[0].url);

              this.setState({
                playlists: {
                  name: plistNames,
                  plist_id: plistContents,
                  albumArt: plistArts
                }
              });
            });
          }
        }
      })
      .catch(err => {
        console.error(err);
      });
  }

  render() {
    //Produces a list of empty images with only the alt attribute showing up
    let _playlists = this.state.playlists.albumArt.map((plist, index) => {
      return <img key={index} src={plist.albumArt} alt="Album Art"/>
    })
  return (
      <div className="App">
        <button onClick={() => this.getPlaylists(someData)}>
          Get Playlist
        </button>

      {_playlists}

      {/*  THIS PRODUCES a similar sympton as the former
          {playlists.albumArt.map((plist, index) => {
              <img key={index}  src={plist.albumArt} alt="Album Art"/>
              })
          } */}

      {/*  THIS IMAGE RENDERS THE FIRST ELEMENT FROM THE ARRAY AND WORKS FINE
            <img src={playlists.albumArt[0]}/> */}

      </div>
    );
  }
}

How does one tackle an issue like this? Thanks a mil for your time and help.

Upvotes: 2

Views: 285

Answers (4)

Pineda
Pineda

Reputation: 7593

You are currently mapping over your array of album art using:

let _playlists = this.state.playlists.albumArt.map((plist, index) => {
  return <img key={index} src={plist.albumArt} alt="Album Art"/>
})

The issue is that your setting the src of your <img /> tags to plist.albumArt when in fact the value of ‘plist’ is the url you want:

let _playlists = this.state.playlists.albumArt.map((plist, index) => {
  return <img key={index} src={plist} alt="Album Art"/>
})

As others have mentioned, it is also not recommended to be setting state during a loop. Rather loop/map through the data and store it in a local variable which you can use to call setState once.

Upvotes: 1

maxwell
maxwell

Reputation: 2381

I can't say for sure because I would need to see the structure of the data being returned but I'm fairly confident you do not want to be setting state within the forEach() loop a bunch of times.

Instead you probably want to map over the results of the data and transform it into some useful array of objects before adding it to state so that you can map over it in the render ( which looks fine ) and access the contents predictably.

That being said I would expect your .then() block after searchPlaylists call to look more like:

const playlistsWrangled =  data.playlists.items.map(plist => ({ 
  name: plist.name, 
  plist_id: plist.id, 
  albumArt: plist.images[0].url 
}));

this.setState({ playlists: wrangledPlaylists });

Upvotes: 1

DoHn
DoHn

Reputation: 673

I believe the difference between your two cases is that in the "working" one, you don't have an alt prop.

You have to cut some corners if you want images to display cleanly during load. Either use modules that progressively handle image loading for you, or, if you don't care about accessibility, make the alt tag empty and give the image a default background color (if you want you can set the alt tag after loading).

Upvotes: 1

lakerskill
lakerskill

Reputation: 1079

What about a conditional inline statement? That way it will render after it receives the information?

    {this.state.playlist.name ?  
    //have your code here to display
    :
    <LoadingSpinner />
}

Upvotes: 1

Related Questions