Achoi98
Achoi98

Reputation: 215

Reactjs not re-rendering after this.setState()

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;

Upvotes: 0

Views: 164

Answers (2)

Gopinath
Gopinath

Reputation: 4957

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

Upvotes: 1

dave
dave

Reputation: 64707

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.

Upvotes: 1

Related Questions