0

I am using spotify's api and retrieving an array of strings to set into the state in order to be mapped into the HTML via JSX.

Logging it to the console shows that I do get the correct array stored into the state, but React never re-renders to display them. If I would setstate again after with my own values, the page works just fine. Maybe it is a problem with async?

import React from 'react';
import Button from 'react-bootstrap/esm/Button';
import Spotify from 'spotify-web-api-js';
import Track from './Track';
import Api from '../Api.js'

export default class OtherPage extends React.Component{

    constructor(props){
        super(props);
        this.state = {
            artists:['artists']
        };
        this.getArtists = this.getArtists.bind(this);
    }

    async getArtists(){
        let api = new Api();
        let arr = await api.getTopArtists();
        console.log('arr', arr);
      
        this.setState({artists: arr});
        console.log('new-arr', this.state.artists);

        // this.setState({artists: ['noe', 'tow', 'tre']})

        // console.log('new-new-arr', this.state.artists)
    }

    render(){
        return(
        <div>
            <h1>My Spotify React App!</h1>
            <div className="tracks-container" style={{maxHeight: 500, overflow: 'scroll', margin:50, marginTop:25}}>

                {this.state.artists.map((artist) => 
                    <p>Artist: {artist}</p>
                )}

            <button onClick={() => this.getArtists()}>Get Top Artists</button>
            </div>
        </div>
        );
    };
}

here is the code for getTopArtists()

import React from 'react';
import { Component } from 'react';
import Button from 'react-bootstrap/Button';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Redirect, BrowserRouter as Router, Route, Switch, useHistory } from 'react-router-dom';

class Api extends React.Component{

  async getTopArtists(){
    let arr = [];
    let artists = await fetch("https://api.spotify.com/v1/me/top/artists", {
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer '+ localStorage.getItem('access_token')
            }
        }
    ).then((response) => {
        console.log(response.json().then(
            (data) => data.items.forEach(item => arr.push(item.name))
        )
        )});
    console.log(arr);
    return arr;
}
  
 



}

export default Api;

2 Answers 2

1

It's hard to say for sure, but two changes I would try:

First,

this.setState({artists: [...arr]});

this forces a new array to be created, just in case the api.getTopArtists(); is somehow reusing the same array for it's results, which could cause React to not detect the change.

second,

{this.state.artists.map((artist) => 
    <p key={artist}>Artist: {artist}</p>
)}

Since without a key on a list, it's harder for react to know what changed in the list when the backing array changes. Probably not the issue, but could be.

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

5 Comments

hmmm... could you try doing console.log('arr', JSON.stringify(arr)); and tell me what you get
all i get is arr[]. i realized that what im getting is an object of length 0 that has an array of the names and now im trying to figure out how to extract the array
It sounds like your getTopArtists function returns the array before it's populated it, but then populates it, which is why the console.log works but react isn't detecting it. Can you post the getTopArtists code?
I just added it!
Sorry I'm on mobile right now, but do let response = await fetch let json = await response.json() json.items.forEach - get rid of all the .then in that function, it should fix it for sure
1

React has a special method named 'componentDidMount()' for the purpose of making calls to external APIs.

Calling the external API and setState subsequently from the componentDidMount() method will help achieve the desired result.

Working example with componentDidMount() :

export default class OtherPage extends React.Component{

    constructor(props){
        super(props)
        this.state = {
            artists:['artists']
        }
    }

    componentDidMount() {
        let api = new Api()
        api.getTopArtists()
           .then((arr) => {
               this.setState({artists: arr}) 
           })
    }

    render() {
        return(
            <div>
                {this.state.artists.map((artist) => 
                    <p>Artist: {artist}</p>
                )}
            </div>
        )
    }
}

More information:

https://reactjs.org/docs/faq-ajax.html

1 Comment

I am using a button to call the api so i dont think componentDidMount() fits my use case

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.