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

问题:

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.


回答1:

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.


回答2:

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 />
}

回答3:

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


回答4:

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 });
  • 发表于 2019-03-15 00:10
  • 阅读 ( 49 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除