supavillain
supavillain

Reputation: 1

Modifying a Fetch result in api

I am beginner React developer and I've run into this problem while fetching API. The API that I'm using returns an array of objects of different characters. The problem is that some of the parameters of an object have a link as their value, meaning that I can't just render

with a key and it's value. Here is a snippet of an object from this API:

{
    "url":"link",
    "name":"Alys Karstark",
    "gender":"Female",
    "culture":"Northmen",
    "born":"",
    "titles":["Lady of Winterfell"],
    "spouse":"link",
    "allegiances":["link",
    "link"],
    "books":["link"],
}

So, I figured I would build a method that upon the arrival of the original list would go through the character objects and go through the keys that have links in them and fetch said links, pulling the name from them for display. After that, I pull a random object of a character from the sorted list and display it.

Here's the fetch upon the mount:

componentDidMount(){
  fetch(defaultQuery)
  .then(response => response.json())
  .then(result => {
    this.setState({result: result})
    this.optimizeCharacterInfo()
  })
  .catch(error => error)
}

And here's the function that does the optimisation:

getRandomChar(){
  this.setState({randomCharacter: _.sample(this.state.optimizedList)})
}

 optimizeCharacterInfo() {
    const optimizedList = this.state.result.map(char => {
      for (let key in char) {
        if (linkKeys.includes(key)) {
          if (!Array.isArray(char[key])) char[key] = [char[key]];
          const names = [];
          char[key].forEach(link => {
            fetch(link)
              .then(response => response.json())
              .then(result => {
                names.push(result.name);
                char[key] = names;
              })
              .catch(error => error);
          });
        }
      }
      char.isOptimized = true;
      return char;
    });
    this.setState({ optimizedList: optimizedList });
    this.getRandomChar();
  }

  render() {
    const { randomCharacter } = this.state;
    return (
      <div>
        <Header headerText={homeText} />
        {randomCharacter && randomCharacter.isOptimized ? (
          <MainPage randomCharacter={randomCharacter} />
        ) : null}
      </div>
    );
  }

Though for some reason whenever I load up the page this is what it looks like:

name: Alyn Velaryon
gender: Male
culture: Valyrian
born: In 115 AC or later, at Hull, Driftmark
died: In or between 171 AC and 176 AC, at sea
titles: Lord of the TidesMaster of DriftmarkAdmiralMaster of ships
aliases: Alyn of HullThe OakenfistLord Oakenfist
father:
mother:
spouse: link
allegiances: link

and I am expecting:

name: Alyn Velaryon
gender: Male
culture: Valyrian
born: In 115 AC or later, at Hull, Driftmark
died: In or between 171 AC and 176 AC, at sea
titles: Lord of the TidesMaster of DriftmarkAdmiralMaster of ships
aliases: Alyn of HullThe OakenfistLord Oakenfist
father:
mother:
spouse: name of the spouse
allegiances: allegiance 1, allegiance 2

Meaning that it did not use the filtered list. Once I look up through the dev tools both the list and the random character have all the links transfered into fetched names. So what gives? I'm going to guess that this is solvable by doing a promise, because if I set a timeout on setting the random character it works, but no matter how much i try I cannot get a working promise. How do I get the function to set the isOptimized to true ONLY when all the links needed are fetched?

Upvotes: 0

Views: 497

Answers (1)

Gabriele Petrioli
Gabriele Petrioli

Reputation: 196002

setState is async, so calling the optimizeCharacterInfo right after it will not do what you want, as the result is not yet in the state.

Luckily setState takes a second parameter, which is run after the state is set.

so change

.then(result => {
    this.setState({result: result})
    this.optimizeCharacterInfo()
  })

to

.then(result => {
    this.setState({result: result}, this.optimizeCharacterInfo)
  })

and the same goes for the optimizeCharacterInfo so change also

this.setState({ optimizedList: optimizedList })
this.getRandomChar()

to

this.setState({ optimizedList: optimizedList }, this.getRandomChar)

Upvotes: 1

Related Questions